From a3413b2e27da48d5ddb836b1eb26e76b9d2289e2 Mon Sep 17 00:00:00 2001 From: Katya Sarmiento <5871075+Kitkatnik@users.noreply.github.com> Date: Sun, 26 Apr 2026 01:18:11 -0400 Subject: [PATCH 1/2] Filter schedule by kind; restyle day-nav and headers to match brand site Adds a Kind filter (All / Talk / Lightning / Embassy / Activity) to both the public Schedule page and the admin Schedule Items page, backed by a junk-safe `by_kind` scope that returns all rows when the URL param is missing or unknown. Filter state lives in the query string so filtered URLs are shareable. Day section visuals now mirror blueridgeruby.com/schedule: combined "Wednesday, April 29th" heading with ordinal sup, 4px navy item-table top-border, and plain underlined day-nav links (no pills). Kind filter keeps a pill treatment because its active/inactive state is meaningful. --- app/assets/stylesheets/application.css | 86 +++++++++++------- .../admin/schedule_items_controller.rb | 3 +- app/controllers/schedule_controller.rb | 5 +- app/models/schedule_item.rb | 4 + app/views/admin/schedule_items/index.html.erb | 89 ++++++++++--------- app/views/schedule/_day.html.erb | 15 ++-- app/views/schedule/index.html.erb | 27 ++++-- app/views/shared/_kind_filter.html.erb | 19 ++++ 8 files changed, 156 insertions(+), 92 deletions(-) create mode 100644 app/views/shared/_kind_filter.html.erb diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 9eb53b6..e92e934 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -347,71 +347,91 @@ hr { .day-nav { display: flex; justify-content: center; + column-gap: 1rem; + margin: 0 0 2.5rem; +} + +.day-nav a { + padding: 0.5rem; + color: #2672B5; + font-weight: 500; + text-decoration: underline; +} + + +/* ---------- Kind filter pills ----------------------------- */ + +.kind-filter { + display: flex; + justify-content: center; + align-items: center; flex-wrap: wrap; gap: 0.5rem; - margin: 1rem 0 3rem; + margin: 1rem 0 2rem; } -.day-nav a { +.kind-filter__label { + font-size: 0.95rem; + font-weight: 500; + color: #7a8189; + margin-right: 0.25rem; +} + +.kind-filter__btn { display: inline-block; padding: 0.375rem 1rem; border: 1px solid #d9dee1; border-radius: 999px; + background-color: transparent; color: #2672B5; font-weight: 500; font-size: 0.95rem; - transition: background-color 0.15s ease, border-color 0.15s ease; + transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease; } -.day-nav a:hover { +.kind-filter__btn:hover { background-color: #f4f7fa; border-color: #2672B5; opacity: 1; } - -/* ---------- Schedule day sections ------------------------- */ - -.schedule-day { - padding-top: 2rem; - border-top: 1px solid #d9dee1; +.kind-filter__btn--active { + background-color: #0C2866; + border-color: #0C2866; + color: #ffffff; } -.schedule-day + .schedule-day { - margin-top: 3rem; +.kind-filter__btn--active:hover { + background-color: #0C2866; + border-color: #0C2866; + color: #ffffff; } -.schedule-day__header { - display: flex; - flex-direction: column; - gap: 0.25rem; - margin-bottom: 1.5rem; -} -@media (min-width: 640px) { - .schedule-day__header { - flex-direction: row; - align-items: baseline; - gap: 1rem; - } +/* ---------- Schedule day sections (blueridgeruby.com style) -- */ + +.schedule-day { + margin-bottom: 5rem; } -.schedule-day__label { +.schedule-day__heading { font-size: 1.75rem; font-weight: 500; color: #0C2866; - line-height: 1.1; + text-align: center; + line-height: 1.2; + margin-bottom: 1rem; } -.schedule-day__date { - font-size: 1.125rem; - color: #404040; +.schedule-day__heading sup { + font-size: 0.6em; + vertical-align: super; + margin-left: 0.05em; } -.schedule-day__subtitle { - font-size: 0.95rem; - color: #7a8189; - font-style: italic; +.schedule-day__items { + border-top: 4px solid #0C2866; + padding-top: 0.25rem; } diff --git a/app/controllers/admin/schedule_items_controller.rb b/app/controllers/admin/schedule_items_controller.rb index 02e6ff0..25371d6 100644 --- a/app/controllers/admin/schedule_items_controller.rb +++ b/app/controllers/admin/schedule_items_controller.rb @@ -3,7 +3,8 @@ class ScheduleItemsController < AdminController before_action :set_schedule_item, only: %i[edit update destroy] def index - @schedule_items = ScheduleItem.ordered + @selected_kind = ScheduleItem.kinds.key?(params[:kind].to_s) ? params[:kind] : nil + @schedule_items = ScheduleItem.by_kind(@selected_kind).ordered end def new diff --git a/app/controllers/schedule_controller.rb b/app/controllers/schedule_controller.rb index a4bddab..b7f6c48 100644 --- a/app/controllers/schedule_controller.rb +++ b/app/controllers/schedule_controller.rb @@ -1,6 +1,7 @@ class ScheduleController < ApplicationController def index - @items_by_day = ScheduleItem.public_items.ordered.group_by(&:day) - @planned_ids = current_user.plan_items.pluck(:schedule_item_id).to_set + @selected_kind = ScheduleItem.kinds.key?(params[:kind].to_s) ? params[:kind] : nil + @items_by_day = ScheduleItem.public_items.by_kind(@selected_kind).ordered.group_by(&:day) + @planned_ids = current_user.plan_items.pluck(:schedule_item_id).to_set end end diff --git a/app/models/schedule_item.rb b/app/models/schedule_item.rb index 72922b5..c277424 100644 --- a/app/models/schedule_item.rb +++ b/app/models/schedule_item.rb @@ -32,6 +32,10 @@ class ScheduleItem < ApplicationRecord :sort_time ) } + # Junk-safe: returns all rows when kind is blank or unknown. + scope :by_kind, ->(kind) { + kind.present? && kinds.key?(kind.to_s) ? where(kind: kind) : all + } # Creators always get auto-added to their own plan — whether the item is # private (only they see it) or public (others can RSVP). The rationale: diff --git a/app/views/admin/schedule_items/index.html.erb b/app/views/admin/schedule_items/index.html.erb index 130bf51..6f44d52 100644 --- a/app/views/admin/schedule_items/index.html.erb +++ b/app/views/admin/schedule_items/index.html.erb @@ -5,45 +5,54 @@ <%= link_to "Add item", new_admin_schedule_item_path, class: "btn btn-red" %> - - - - - - - - - - - - - - <% @schedule_items.each do |item| %> +<%= render "shared/kind_filter", base_path: admin_schedule_items_path, selected_kind: @selected_kind %> + +<% if @schedule_items.empty? %> +

+ No items match this filter. + <%= link_to "Clear filter", admin_schedule_items_path, class: "text-blue underline" %> +

+<% else %> +
DayTimeInfoKindPublic?Host
+ - - - - - - - + + + + + + + - <% end %> - -
<%= ScheduleItem::DAY_META.dig(item.day, :label) || item.day %><%= item.time_label %> -
<%= item.title %>
- <% if item.location.present? %> -
<%= item.location %>
- <% end %> - <% if item.description.present? %> -
<%= item.description %>
- <% end %> -
<%= item.kind.humanize %><%= item.is_public? ? "Public" : "Private" %><%= item.host.presence || "—" %> -
- <%= link_to "Edit", edit_admin_schedule_item_path(item), class: "btn btn-muted" %> - <%= button_to "Delete", admin_schedule_item_path(item), - method: :delete, - data: { turbo_confirm: "Delete '#{item.title}'? RSVPs will also be removed." }, - class: "btn btn-red" %> -
-
DayTimeInfoKindPublic?Host
+ + + <% @schedule_items.each do |item| %> + + <%= ScheduleItem::DAY_META.dig(item.day, :label) || item.day %> + <%= item.time_label %> + +
<%= item.title %>
+ <% if item.location.present? %> +
<%= item.location %>
+ <% end %> + <% if item.description.present? %> +
<%= item.description %>
+ <% end %> + + <%= item.kind.humanize %> + <%= item.is_public? ? "Public" : "Private" %> + <%= item.host.presence || "—" %> + +
+ <%= link_to "Edit", edit_admin_schedule_item_path(item), class: "btn btn-muted" %> + <%= button_to "Delete", admin_schedule_item_path(item), + method: :delete, + data: { turbo_confirm: "Delete '#{item.title}'? RSVPs will also be removed." }, + class: "btn btn-red" %> +
+ + + <% end %> + + +<% end %> diff --git a/app/views/schedule/_day.html.erb b/app/views/schedule/_day.html.erb index 79765e8..33413b3 100644 --- a/app/views/schedule/_day.html.erb +++ b/app/views/schedule/_day.html.erb @@ -1,11 +1,12 @@ +<% + month, day_num_str = meta[:date].to_s.split(" ", 2) + day_num = day_num_str.to_i + ordinal_suffix = day_num.ordinalize.delete("0-9") +%>
-
-

<%= meta[:label] %>

- <%= meta[:date] %> - <% if meta[:subtitle].present? %> - <%= meta[:subtitle] %> - <% end %> -
+

+ <%= meta[:label] %>, <%= month %> <%= day_num %><%= ordinal_suffix %> +

<% items.each do |item| %> diff --git a/app/views/schedule/index.html.erb b/app/views/schedule/index.html.erb index b7faac1..17333d6 100644 --- a/app/views/schedule/index.html.erb +++ b/app/views/schedule/index.html.erb @@ -10,17 +10,26 @@ Browse the full conference agenda. Tap Add to plan on any talk or RSVP on any activity to save it to your Plan.

- - - <% ScheduleItem::DAY_META.each do |day_key, meta| %> - <% items = @items_by_day[day_key] %> - <% next if items.blank? %> - <%= render "day", day_key: day_key, meta: meta, items: items, planned_ids: @planned_ids %> <% end %>
diff --git a/app/views/shared/_kind_filter.html.erb b/app/views/shared/_kind_filter.html.erb new file mode 100644 index 0000000..2118eb2 --- /dev/null +++ b/app/views/shared/_kind_filter.html.erb @@ -0,0 +1,19 @@ +<%# + locals: + base_path - String, e.g. schedule_path or admin_schedule_items_path + selected_kind - String or nil, the currently active kind +%> +<% qp_clear = request.query_parameters.except(:kind) %> + From 594d1d327a18304b00c53d7592d6c938b71ef604 Mon Sep 17 00:00:00 2001 From: Katya Sarmiento <5871075+Kitkatnik@users.noreply.github.com> Date: Sun, 26 Apr 2026 01:20:17 -0400 Subject: [PATCH 2/2] Smooth-scroll anchor jumps for the day-nav Adds `scroll-behavior: smooth` on `html`, gated by `prefers-reduced-motion: no-preference` so users who request reduced motion at the OS level keep instant jumps. The day-nav anchor links (and any future in-page anchors) now glide instead of snapping. --- app/assets/stylesheets/application.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index e92e934..53d2332 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -22,6 +22,10 @@ html { -webkit-text-size-adjust: 100%; } +@media (prefers-reduced-motion: no-preference) { + html { scroll-behavior: smooth; } +} + body { min-height: 100vh; } img, svg { display: block; max-width: 100%; }