Skip to content
3 changes: 2 additions & 1 deletion app/controllers/workshop_ideas_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ def set_form_variables
@potential_series_workshops = authorized_scope(Workshop.published).includes(:windows_type).order(:title)
@sectors = Sector.published
@windows_types = WindowsType.all
@organizations = authorized_scope(Organization.all, as: :currently_affiliated).order(:name)
@users = User.has_access.includes(:person).references(:person).order(Arel.sql("LOWER(people.first_name), LOWER(people.last_name), LOWER(users.email), LOWER(people.email_2), LOWER(people.email)"))
@categories_grouped =
Category
Expand All @@ -109,7 +110,7 @@ def set_workshop_idea
def workshop_idea_params
params.require(:workshop_idea).permit(
:title, :staff_notes, :author_credit_preference,
:created_by_id, :updated_by_id, :windows_type_id,
:created_by_id, :updated_by_id, :windows_type_id, :organization_id,
:time_closing, :time_creation, :time_demonstration,
:time_hours, :time_intro, :time_minutes,
:time_opening, :time_opening_circle, :time_warm_up,
Expand Down
3 changes: 2 additions & 1 deletion app/controllers/workshops_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ def set_form_variables
potential_series = potential_series.where.not(id: @workshop.id) if @workshop.persisted?
@potential_series_workshops = authorized_scope(potential_series).order(:title)
@windows_types = WindowsType.all
@organizations = authorized_scope(Organization.all, as: :currently_affiliated).order(:name)
@workshop_ideas = authorized_scope(WorkshopIdea.order(created_at: :desc))
.map { |wi|
[ "#{wi.created_at.strftime("%Y-%m-%d")
Expand Down Expand Up @@ -205,7 +206,7 @@ def dedupe_config
def workshop_params
params.require(:workshop).permit(
:title, :featured, :published,
:full_name, :author_id, :windows_type_id, :workshop_idea_id, :author_credit_preference,
:full_name, :author_id, :windows_type_id, :workshop_idea_id, :organization_id, :author_credit_preference,
:month, :year, :filemaker_code,
:publicly_visible,
:publicly_featured,
Expand Down
23 changes: 22 additions & 1 deletion app/models/organization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,17 @@ def communications_email
end
has_many :reports
has_many :workshop_logs
has_many :workshop_ideas
has_many :workshops
has_many :workshops_as_author, through: :people, source: :workshops_as_author
has_many :workshop_ideas_as_author, through: :people, source: :workshop_ideas_as_author
has_many :grants, as: :funder, dependent: :destroy

has_many :categorizable_items, dependent: :destroy, inverse_of: :categorizable, as: :categorizable
has_many :sectorable_items, as: :sectorable, dependent: :destroy
# has_many through
has_many :categories, through: :categorizable_items
has_many :sectors, through: :sectorable_items
has_many :workshops, through: :users

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this wasn't in the spec, but on org profile we should put both workshops/ideas that are directly connected to the organization, and, also pull through the Affiliation > Person > Workshops/ideas authored (not user_id). would you be ok to add that this this pr so the list that shows on Org profile shows directly and indirectly associated workshops and ideas?


# Asset associations
has_one_attached :logo, dependent: :purge do |attachable|
Expand Down Expand Up @@ -241,6 +244,24 @@ def affiliated_workshop_logs
direct.or(legacy).distinct
end

def affiliated_workshops
return Workshop.none unless persisted?

direct = Workshop.where(organization_id: id)
indirect = Workshop.where(author_id: people.select(:id))
direct.or(indirect).distinct
end
alias_method :all_workshops, :affiliated_workshops

def affiliated_workshop_ideas
return WorkshopIdea.none unless persisted?

direct = WorkshopIdea.where(organization_id: id)
indirect = WorkshopIdea.where(author_id: people.select(:id))
direct.or(indirect).distinct
end
alias_method :all_workshop_ideas, :affiliated_workshop_ideas

# Display order, and what the attendees index filters on.
FACILITATOR_PROGRAM_STATUSES = FacilitatorProgramStatus::STATUSES

Expand Down
1 change: 1 addition & 0 deletions app/models/workshop.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def self.mentionable_rich_text_fields
belongs_to :updated_by, class_name: "User", optional: true
belongs_to :author, class_name: "Person", optional: true
belongs_to :workshop_idea, optional: true
belongs_to :organization, optional: true

has_many :bookmarks, as: :bookmarkable, dependent: :destroy
has_many :comments, -> { newest_first }, as: :commentable, dependent: :destroy
Expand Down
5 changes: 5 additions & 0 deletions app/models/workshop_idea.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class WorkshopIdea < ApplicationRecord
belongs_to :created_by, class_name: "User"
belongs_to :updated_by, class_name: "User"
belongs_to :windows_type
belongs_to :organization, optional: true

has_many :bookmarks, as: :bookmarkable, dependent: :destroy
has_many :notifications, as: :noticeable, dependent: :nullify
Expand Down Expand Up @@ -35,6 +36,10 @@ class WorkshopIdea < ApplicationRecord

validates :title, presence: true, uniqueness: { case_sensitive: false }, length: { maximum: 255 }

def name
title
end

# Nested attributes
accepts_nested_attributes_for :primary_asset, reject_if: :all_blank, allow_destroy: true
accepts_nested_attributes_for :gallery_assets, reject_if: :all_blank, allow_destroy: true
Expand Down
3 changes: 2 additions & 1 deletion app/services/workshop_from_idea_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ def call

def attributes_from_idea
workshop_idea.attributes.slice(
"title", "windows_type_id", "author_credit_preference",
"title", "windows_type_id", "organization_id", "author_credit_preference",
"time_intro", "time_closing", "time_creation",
"time_demonstration", "time_warm_up",
"time_opening", "time_opening_circle"
).merge(
organization_id: workshop_idea.organization_id,
rhino_objective: workshop_idea.rhino_objective,
rhino_materials: workshop_idea.rhino_materials,
rhino_optional_materials: workshop_idea.rhino_optional_materials,
Expand Down
24 changes: 20 additions & 4 deletions app/views/organizations/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -227,14 +227,30 @@
<% if @organization.profile_show_workshops? %>
<div>
<h3 class="mb-3 font-display text-xl text-gray-900">Workshops authored</h3>
<% if @organization.workshops.any? %>
<% @organization.workshops.each do |workshop| %>
<%= link_to workshop.name, workshop_path(workshop), class: button_classes(:secondary_outline) %>
<% end %>
<% workshops = @organization.affiliated_workshops %>
<% if workshops.any? %>
<div class="flex flex-wrap gap-2">
<% workshops.each do |workshop| %>
<%= link_to workshop.name, workshop_path(workshop), class: button_classes(:secondary_outline) %>
<% end %>
</div>
<% else %>
<p class="text-gray-500 italic">No workshops authored yet.</p>
<% end %>
</div>
<div>
<h3 class="mb-3 font-display text-xl text-gray-900">Workshop ideas</h3>
<% workshop_ideas = @organization.affiliated_workshop_ideas %>
<% if workshop_ideas.any? %>
<div class="flex flex-wrap gap-2">
<% workshop_ideas.each do |idea| %>
<%= link_to (idea.respond_to?(:name) ? idea.name : idea.title), workshop_idea_path(idea), class: button_classes(:secondary_outline) %>
<% end %>
</div>
<% else %>
<p class="text-gray-500 italic">No workshop ideas yet.</p>
<% end %>
</div>
<% end %>
<!-- Stories authored -->
<% if @organization.profile_show_stories? %>
Expand Down
26 changes: 26 additions & 0 deletions app/views/workshop_ideas/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

<% promoted_to_workshop = f.object.workshops.any? %>
<% if promoted_to_workshop %>
<%= f.hidden_field :organization_id, value: f.object.organization_id %>
<div class="admin-only m-3 bg-yellow-100 p-3">
This workshops idea has been promoted to a
<% f.object.workshops.each do |workshop| %>
Expand Down Expand Up @@ -62,6 +63,31 @@
</div>
</div>

<!-- Organization -->
<div class="mb-6 grid grid-cols-1 gap-6 md:grid-cols-2">
<div>
<% organizations = @organizations || [] %>
<% if organizations.size == 1 %>
<%= f.hidden_field :organization_id,
value: f.object.organization_id || organizations.first.id %>
<% else %>
<%= f.input :organization_id,
as: :select,
collection: organizations,
label_method: :type_name,
value_method: :id,
label: "Organization",
required: false,
disabled: promoted_to_workshop,
include_blank: "Select organization",
selected: (params[:organization_id].presence || f.object.organization_id),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need include_blank: "Select organization" so they can remove an org/leave it blank

hint: "Please contact AWBW if your organization is not listed.",
input_html: { class: "block w-full rounded-md border-gray-300 shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm #{"readonly" if promoted_to_workshop}" },
label_html: { class: "block text-sm font-medium text-gray-700 mb-1" } %>
<% end %>
</div>
</div>

<% if allowed_to?(:manage?, WorkshopIdea) %>
<div class="rounded bg-white pb-6">
<div class="admin-only flex items-center gap-x-2 bg-blue-100 p-3">
Expand Down
2 changes: 2 additions & 0 deletions app/views/workshop_ideas/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<th class="w-1/6 px-4 py-2 text-left text-sm font-semibold text-gray-700">Title</th>
<th class="w-1/6 px-4 py-2 text-left text-sm font-semibold text-gray-700">Timeframe</th>
<th class="w-1/6 px-4 py-2 text-left text-sm font-semibold text-gray-700">Author</th>
<th class="w-1/6 px-4 py-2 text-left text-sm font-semibold text-gray-700">Organization</th>
<th class="w-[60px] px-4 py-2 text-center text-sm font-semibold text-gray-700">Promoted?</th>
<th class="w-[80px] px-4 py-2 text-center text-sm font-semibold text-gray-700">Actions</th>
</tr>
Expand All @@ -46,6 +47,7 @@
<td class="px-4 py-2 text-sm text-gray-800"><%= workshop_idea.title %></td>
<td class="px-4 py-2 text-sm text-gray-800"><%= workshop_idea.time_frame_total %></td>
<td class="px-4 py-2 text-sm text-gray-800"><%= credited_author_link(workshop_idea, class: eyebrow_link_class) %></td>
<td class="px-4 py-2 text-sm text-gray-800"><%= workshop_idea.organization&.name %></td>
<td class="px-4 py-2 text-center text-sm text-gray-800">
<% if workshop_idea.workshops.any? %>
<span class='fa fa-check-circle text-green-500'></span>
Expand Down
11 changes: 11 additions & 0 deletions app/views/workshop_ideas/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,17 @@
<span class="font-semibold text-gray-700">Timeframe:</span>
<%= @workshop_idea.timeframe.presence || "—" %>
</div>
<div>
<span class="font-semibold text-gray-700">Organization:</span>
<% if @workshop_idea.organization && allowed_to?(:show?, @workshop_idea.organization) %>
<%= link_to @workshop_idea.organization.name,
organization_path(@workshop_idea.organization),
data: { turbo_frame: "_top" },
class: "inline-block px-3 py-1 rounded-md text-sm font-medium #{DomainTheme.bg_class_for(:organizations, intensity: 100)} #{DomainTheme.text_class_for(:organizations)} #{DomainTheme.bg_class_for(:organizations, intensity: 100, hover: true)}" %>
<% else %>
<%= @workshop_idea.organization&.name || "—" %>
<% end %>
</div>
<div>
<span class="font-semibold text-gray-700">Created by:</span>
<%= person_name(@workshop_idea.created_by) %>
Expand Down
20 changes: 20 additions & 0 deletions app/views/workshops/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,26 @@
hint: "If this workshop was created from a Workshop Idea, select it here.",
input_html: { value: f.object.workshop_idea_id,
class: "block w-full rounded-md border-gray-300 shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm" } %>
<div>
<% organizations = @organizations || [] %>
<% if organizations.size == 1 %>
<%= f.hidden_field :organization_id,
value: f.object.organization_id || organizations.first.id %>
<% else %>
<%= f.input :organization_id,
as: :select,
collection: organizations,
label_method: :type_name,
value_method: :id,
label: "Organization",
required: false,
include_blank: "Select organization",
selected: (params[:organization_id].presence || f.object.organization_id),
hint: "Please contact AWBW if your organization is not listed.",
input_html: { class: "block w-full rounded-md border-gray-300 shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm" },
label_html: { class: "block text-sm font-medium text-gray-700 mb-1" } %>
<% end %>
</div>
<div class="mt-4">
<label class="mb-1 block font-medium text-gray-700">Workshop created</label>
<div class="flex gap-x-2">
Expand Down
3 changes: 3 additions & 0 deletions app/views/workshops/_index_row.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@
<div class="text-sm leading-tight text-gray-600">
By:
<%= credited_author_link(workshop, class: "hover:underline", data: { turbo: false }) %>
<% if workshop.organization.present? %>
| <%= workshop.organization.name %>
<% end %>
| <%= workshop.date %>
</div>

Expand Down
9 changes: 8 additions & 1 deletion app/views/workshops/_show_header.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,14 @@
<span class="text-lg text-red-300">(Unpublished)</span>
<% end %>
</h1>
<div class="mt-3 flex flex-wrap gap-4 text-sm text-gray-200"><span>Author: <%= credited_author_link(workshop, class: "hover:underline") %></span><span>Category: <%= workshop.windows_type&.short_name %></span><span>Added: <%= workshop.date %></span></div>
<div class="mt-3 flex flex-wrap gap-4 text-sm text-gray-200">
<span>Author: <%= credited_author_link(workshop, class: "hover:underline") %></span>
<% if workshop.organization.present? %>
<span>Organization: <%= link_to_if allowed_to?(:show?, workshop.organization), workshop.organization.name, organization_path(workshop.organization), class: "hover:underline" %></span>
<% end %>
<span>Category: <%= workshop.windows_type&.short_name %></span>
<span>Added: <%= workshop.date %></span>
</div>
</div>
<%= render "assets/primary_image_picker", owner: workshop %>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddOrganizationToWorkshopIdeas < ActiveRecord::Migration[8.1]
def change
add_reference :workshop_ideas, :organization, type: :integer, null: true, foreign_key: true
end
end
5 changes: 5 additions & 0 deletions db/migrate/20261018123640_add_organization_to_workshops.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddOrganizationToWorkshops < ActiveRecord::Migration[8.1]
def change
add_reference :workshops, :organization, type: :integer, null: true, foreign_key: true
end
end
6 changes: 6 additions & 0 deletions db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2486,8 +2486,10 @@
t.text "warm_up"
t.text "warm_up_spanish"
t.integer "windows_type_id", null: false
t.integer "organization_id"
t.index ["author_id"], name: "index_workshop_ideas_on_author_id"
t.index ["created_by_id"], name: "index_workshop_ideas_on_created_by_id"
t.index ["organization_id"], name: "index_workshop_ideas_on_organization_id"
t.index ["updated_by_id"], name: "index_workshop_ideas_on_updated_by_id"
t.index ["windows_type_id"], name: "index_workshop_ideas_on_windows_type_id"
end
Expand Down Expand Up @@ -2678,11 +2680,13 @@
t.integer "windows_type_id"
t.bigint "workshop_idea_id"
t.integer "year"
t.integer "organization_id"
t.index ["author_id"], name: "index_workshops_on_author_id"
t.index ["created_at"], name: "index_workshops_on_created_at"
t.index ["created_by_id"], name: "index_workshops_on_created_by_id"
t.index ["inactive", "led_count", "title"], name: "index_workshops_on_inactive_and_led_count_and_title"
t.index ["led_count"], name: "index_workshops_on_led_count"
t.index ["organization_id"], name: "index_workshops_on_organization_id"
t.index ["published"], name: "index_workshops_on_published"
t.index ["title", "full_name", "objective", "materials", "introduction", "demonstration", "opening_circle", "warm_up", "creation", "closing", "notes", "tips", "misc1", "misc2"], name: "workshop_fullsearch", type: :fulltext
t.index ["title"], name: "index_workshops_on_title", type: :fulltext
Expand Down Expand Up @@ -2954,6 +2958,7 @@
add_foreign_key "windows_types", "users", column: "updated_by_id"
add_foreign_key "workshop_age_ranges", "age_ranges"
add_foreign_key "workshop_age_ranges", "workshops"
add_foreign_key "workshop_ideas", "organizations"
add_foreign_key "workshop_ideas", "people", column: "author_id"
add_foreign_key "workshop_ideas", "users", column: "created_by_id"
add_foreign_key "workshop_ideas", "users", column: "updated_by_id"
Expand Down Expand Up @@ -2981,6 +2986,7 @@
add_foreign_key "workshop_variations", "windows_types"
add_foreign_key "workshop_variations", "workshop_variation_ideas"
add_foreign_key "workshop_variations", "workshops"
add_foreign_key "workshops", "organizations"
add_foreign_key "workshops", "people", column: "author_id"
add_foreign_key "workshops", "users", column: "created_by_id"
add_foreign_key "workshops", "users", column: "updated_by_id"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
name: "Workshops and ideas on organization profiles"
area: content
display_status: user_facing
released_on: 2026-10-22
action_path: "/organizations"
summary: >-
Workshops and workshop ideas now link to organizations directly and display on
organization profiles, including content authored by affiliated facilitators.
2 changes: 1 addition & 1 deletion spec/factories/workshop_ideas.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
FactoryBot.define do
factory :workshop_idea do
title { "MyString" }
sequence(:title) { |n| "Workshop Idea #{n}" }
description { "MyText" }
staff_notes { "MyText" }
association :windows_type
Expand Down
Loading
Loading