Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 45 additions & 2 deletions app/decorators/person_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,52 @@ def default_display_image
"missing.png"
end

def facilitator_since_date
facilitator_affiliations = affiliations.where("title LIKE ?", "%Facilitator%")
facilitator_affiliations.minimum(:start_date) || member_since
end

def affiliated_since_date
affiliations.minimum(:start_date)
end

def affiliated_end_date
return nil if affiliations.active.exists?
affiliations.maximum(:end_date)
end

def facilitator_end_date
facilitator_affiliations = affiliations.where("title LIKE ?", "%Facilitator%")
return nil if facilitator_affiliations.active.exists?
facilitator_affiliations.maximum(:end_date)
end

def member_since_year
member_since ? member_since.year : nil
facilitator_since_date&.year
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

we're moving forward assuming member_since meant facilitator since, and introducing affiliated since.

end

def member_since_earlier_than_facilitator_affiliations?
earliest_facilitator = affiliations.where("title LIKE ?", "%Facilitator%").minimum(:start_date)
member_since.present? && earliest_facilitator.present? && member_since.beginning_of_month < earliest_facilitator.beginning_of_month
end

def member_since_earlier_than_all_affiliations?
earliest = affiliations.minimum(:start_date)
member_since.present? && earliest.present? && member_since.beginning_of_month < earliest.beginning_of_month
end

def member_since_differs_from_facilitator_affiliations?
earliest_facilitator = affiliations.where("title LIKE ?", "%Facilitator%").minimum(:start_date)
member_since.present? && earliest_facilitator.present? && member_since.beginning_of_month != earliest_facilitator.beginning_of_month
end

def member_since_differs_from_all_affiliations?
earliest = affiliations.minimum(:start_date)
member_since.present? && earliest.present? && member_since.beginning_of_month != earliest.beginning_of_month
end

def badges
earliest = affiliations.minimum(:start_date) || member_since
earliest = facilitator_since_date
years = earliest ? (Time.zone.now.year - earliest.year) : nil
badges = []
badges << badge("Legacy Facilitator (10+ years)", :legacy_facilitator) if years && years >= 10
Expand All @@ -43,6 +83,9 @@ def badges
badges << badge("Story Author", :stories) if user&.stories_as_creator&.any?
badges << badge("Sector Leader", :sectors) if sectorable_items.where(is_leader: true).any?
badges << badge("Blog Contributor", :blog_contributor) if blog_contributor?
if affiliated_since_date.present? && affiliated_since_date != facilitator_since_date
badges << badge("Affiliated since #{affiliated_since_date.strftime('%B %Y')}", :affiliated_person)
end
badges
end

Expand Down
9 changes: 7 additions & 2 deletions app/models/organization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,11 @@ class Organization < ApplicationRecord

# Scopes
# See TagFilterable, Trendable, WindowsTypeFilterable
scope :active, -> { joins(:organization_status).where(organization_statuses: { name: "Active" }) }
scope :active, -> {
status_active = joins(:organization_status).where(organization_statuses: { name: "Active" })
affiliation_active = where(id: Affiliation.active.select(:organization_id))
status_active.or(affiliation_active)
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

using affiliations to determine active status now

scope :address, ->(address) do
return all if address.blank?
terms = address.to_s.strip.split(/[\s,]+/).reject(&:blank?)
Expand Down Expand Up @@ -141,7 +145,8 @@ def organization_locality
end

def published? # needed for my_bookmarks
organization_status&.name == "Active"
return true if organization_status&.name == "Active"
affiliations.active.exists?
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

this has potential to be an issue. will leave it like this for now.

end

def sector_list
Expand Down
184 changes: 107 additions & 77 deletions app/views/organizations/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,61 @@
</div>

<!-- Classification -->
<% org_earliest_aff = f.object.persisted? ? f.object.affiliations.minimum(:start_date) : nil %>
<% org_aff_ended = f.object.persisted? && f.object.affiliations.any? && !f.object.affiliations.active.exists? %>
<% org_latest_end = f.object.persisted? ? f.object.affiliations.maximum(:end_date) : nil %>
<% org_end_date = org_aff_ended ? org_latest_end : f.object.end_date %>
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
<% if allowed_to?(:manage?, Organization) %>
<div class="">
<div class="shrink-0 relative group cursor-help">
<label class="inline-flex items-center gap-1 text-md font-medium text-gray-700 mb-1">
Affiliated since <i class="fa-solid fa-circle-info text-gray-400 text-xs"></i>
</label>
<div class="hidden group-hover:block absolute z-50 left-0 bottom-full mb-1 bg-blue-100 text-gray-700 text-xs rounded-lg shadow-lg ring-1 ring-blue-200 p-3 w-64 whitespace-normal">
<% if has_affiliations && org_earliest_aff.present? %>
<p>Auto-managed from affiliation records. Earliest affiliation start date: <%= org_earliest_aff.strftime('%b %Y') %>.</p>
<% if f.object.start_date.present? && f.object.start_date.beginning_of_month != org_earliest_aff.beginning_of_month %>
<p class="mt-2 text-gray-500"><i class="fa-solid fa-circle-info mr-1"></i>Organization start_date (<%= f.object.start_date.strftime('%b %Y') %>) differs from earliest affiliation.</p>
<% end %>
<% elsif has_affiliations %>
<p>Auto-managed from affiliation records.<br><br><span class="text-amber-700 font-medium">⚠ No affiliations have a start date. Add a start date to an affiliation record.</span></p>
<% else %>
<p>No affiliations yet. Set this date directly, or add affiliation records to auto-manage it.</p>
<% end %>
<% if org_aff_ended %>
<p class="mt-2 text-amber-700 font-medium">⚠ All affiliations have ended.</p>
<% elsif f.object.end_date.present? && !has_affiliations %>
<p class="mt-2 text-amber-700 font-medium">⚠ Organization end_date: <%= f.object.end_date.strftime('%b %Y') %>.</p>
<% end %>
</div>
<div class="pt-1">
<% if org_aff_ended || (f.object.end_date.present? && !has_affiliations) %>
<i class="fa-solid fa-circle-xmark text-red-400 mr-1" title="Affiliation ended"></i>
<% end %>
<span class="text-gray-900 font-medium">
<%= (org_earliest_aff || f.object.start_date)&.strftime('%b %Y') || "—" %><%= " – #{org_end_date.strftime('%b %Y')}" if org_end_date.present? %>
</span>
<% if has_affiliations && org_earliest_aff.nil? %>
<i class="fa-solid fa-triangle-exclamation text-amber-500 ml-1"></i>
<% elsif f.object.start_date.present? && org_earliest_aff.present? && f.object.start_date.beginning_of_month != org_earliest_aff.beginning_of_month %>
<p class="text-xs text-gray-500 mt-1"><i class="fa-solid fa-circle-info mr-1"></i>Organization start_date: <%= f.object.start_date.strftime("%b %Y") %></p>
<% end %>
</div>
</div>
<% unless has_affiliations %>
<%= f.input :start_date,
label: false,
as: :string,
input_html: {
type: 'date',
value: f.object.start_date&.strftime('%Y-%m-%d'),
class: "block w-full rounded-md border-gray-300 shadow-sm focus:ring-blue-500 focus:border-blue-500 mt-2"
} %>
<% end %>
</div>
<% end %>

<div class="flex flex-col">
<%= f.input :agency_type,
label: "Agency Type",
Expand All @@ -89,106 +143,82 @@
value: f.object.agency_type,
class: "rounded-md border-gray-300 shadow-sm focus:ring-blue-500 focus:border-blue-500"
} %>
<%= f.input :agency_type_other,
label: "Agency Type Other",
hint: "Please specify if 'Other' selected",
input_html: {
class: "rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-200"
} %>
</div>

<%= f.input :agency_type_other,
label: "Agency Type Other",
hint: "Please specify if 'Other' selected",
input_html: {
class: "rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-200"
} %>
<%= f.input :email,
label: "Email",
as: :email,
input_html: {
class: "rounded-md border-gray-300 shadow-sm focus:ring-blue-500 focus:border-blue-500"
} %>
<div class="flex flex-col">
<%= f.input :email,
label: "Email",
as: :email,
input_html: {
class: "rounded-md border-gray-300 shadow-sm focus:ring-blue-500 focus:border-blue-500"
} %>
<% if f.object.website_url.present? && (URI.parse(f.object.website_url) rescue nil).nil? %>
<% website_warning = "<span class='text-red-600 bg-red-50 border border-red-200 rounded px-3 py-2 text-sm''>
Invalid</span>" %>
<% end %>
<%= f.input :website_url,
label: "Website URL #{website_warning}".html_safe,
as: :string,
hint: "Enter full URL: https:// or http://".html_safe,
wrapper_html: { id: "website-url-field" },
input_html: {
class: "rounded-md #{"bg-red-200" if website_warning.present? } border-gray-300
shadow-sm focus:ring-blue-500 focus:border-blue-500"
} %>
</div>

<% if f.object.website_url.present? && (URI.parse(f.object.website_url) rescue nil).nil? %>
<% website_warning = "<span class='text-red-600 bg-red-50 border border-red-200 rounded px-3 py-2 text-sm''>
Invalid</span>" %>
<% if allowed_to?(:manage?, Organization) %>
<div class="admin-only bg-blue-100">
<%= f.input :internal_id,
label: "Internal Organization ID",
input_html: {
class: "rounded-md border-gray-300 bg-blue-100 shadow-sm focus:ring-blue-500 focus:border-blue-500"
} %>
</div>
<% end %>
<%= f.input :website_url,
label: "Website URL #{website_warning}".html_safe,
as: :string,
hint: "Enter full URL: https:// or http://".html_safe,
wrapper_html: { id: "website-url-field" },

<%= f.input :organization_status_id,
as: :select,
collection: @organization_statuses,
include_blank: true,
label: "Status",
required: true,
input_html: {
class: "rounded-md #{"bg-red-200" if website_warning.present? } border-gray-300
shadow-sm focus:ring-blue-500 focus:border-blue-500"
class: "rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-200"
} %>
</div>

<!-- Affiliation dates & admin fields -->
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
<% if allowed_to?(:manage?, Organization) %>
<div class="admin-only bg-blue-100">
<% if params[:admin] && allowed_to?(:manage?, Organization) %>
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
<div class="admin-only bg-blue-100 p-3">
<label class="block text-md font-medium text-gray-700 mb-1">Affiliation start date</label>
<%= f.input :start_date,
label: "Affiliation start date",
label: false,
as: :string,
hint: automanaged_notice.to_s.html_safe,
input_html: {
type: 'date',
value: f.object.start_date&.strftime('%Y-%m-%d'),
disabled: has_affiliations,
class: "block w-full rounded-md border-gray-300 shadow-sm focus:ring-blue-500 focus:border-blue-500"
} %>
</div>
<div class="admin-only bg-blue-100">
<div class="admin-only bg-blue-100 p-3">
<label class="block text-md font-medium text-gray-700 mb-1">Affiliation end date</label>
<%= f.input :end_date,
label: "Affiliation end date",
label: false,
as: :string,
hint: automanaged_notice.to_s.html_safe,
input_html: {
type: 'date',
value: f.object.end_date&.strftime('%Y-%m-%d'),
disabled: has_affiliations,
class: "block w-full rounded-md border-gray-300 shadow-sm focus:ring-blue-500 focus:border-blue-500"
} %>
</div>
<% if allowed_to?(:manage?, Organization) %>
<div class="admin-only bg-blue-100">
<%= f.input :organization_status_id,
as: :select,
collection: @organization_statuses,
include_blank: true,
label: "Status",
hint: automanaged_notice.to_s.html_safe,
required: true,
input_html: {
class: "rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-200"
} %>
</div>
<% else %>
<div class="text-md font-semibold">
Organization status:
</div>
<%= f.object.organization_status&.name %>
<% end %>
<div class="admin-only bg-blue-100">
<%= f.input :internal_id,
label: "Internal Organization ID",
input_html: {
class: "rounded-md border-gray-300 shadow-sm focus:ring-blue-500 focus:border-blue-500"
} %>
</div>
<% else %>
<div>
<div class="text-md font-semibold">
Affiliation start date:
</div>
<%= f.object.start_date&.strftime('%Y-%m-%d') %>
<%= automanaged_notice.to_s.html_safe %>
</div>
<div>
<div class="text-md font-semibold">
Affiliation end date:
</div>
<%= f.object.end_date&.strftime('%Y-%m-%d') %>
<%= automanaged_notice.to_s.html_safe %>
</div>
<% end %>
</div>
</div>
<% end %>

<!-- Mission/Vision/Values -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
Expand Down
2 changes: 1 addition & 1 deletion app/views/organizations/organization_results.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@

<td class="px-4 py-2 text-sm text-gray-700 whitespace-nowrap">
<% affiliated_since = @affiliated_since[organization.id] || organization.start_date %>
<%= affiliated_since&.year %>
<%= affiliated_since&.strftime('%b %Y') %>
</td>

<td class="px-4 py-2 text-center text-sm">
Expand Down
37 changes: 30 additions & 7 deletions app/views/organizations/show.html.erb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<% content_for(:page_bg_class, "admin-or-owner") %>
<div class="admin-or-owner <%#= DomainTheme.bg_class_for(:organizations) %> border border-gray-200 rounded-xl shadow overflow-hidden">
<% content_for(:page_bg_class, "admin-or-auth") %>
<div class="border border-gray-200 rounded-xl shadow overflow-hidden">
<!-- Organization header banner -->
<div class="<%= DomainTheme.bg_class_for(:organizations, intensity: 100) %> border-b <%= DomainTheme.border_class_for(:organizations, intensity: 300) %> px-8 py-3">
<div class="flex items-center gap-2">
Expand Down Expand Up @@ -52,11 +52,34 @@
<%= [address.locality, [address.city, address.state].compact_blank.join(", "), address.district].compact_blank.join(" · ") %>
</p>
<% end %>
<% if @organization.start_date.present? %>
<p class="mt-1 text-gray-500 text-sm">
Affiliated since
<span class="font-medium"><%= @organization.start_date.year %></span>
</p>
<% org_earliest_affiliation = @organization.affiliations.minimum(:start_date) %>
<% org_affiliated_since = org_earliest_affiliation || @organization.start_date %>
<% org_show_aff_ended = @organization.affiliations.any? && !@organization.affiliations.active.exists? %>
<% org_show_end_date = org_show_aff_ended ? @organization.affiliations.maximum(:end_date) : @organization.end_date %>
<% if org_affiliated_since.present? %>
<div class="relative group cursor-help inline-block mt-1">
<p class="text-gray-500 text-sm">
<% if org_show_aff_ended || org_show_end_date.present? %>
<i class="fa-solid fa-circle-xmark text-red-400 mr-1"></i>
<% end %>
Affiliated since
<span class="font-medium"><%= org_affiliated_since.strftime("%b %Y") %><%= " – #{org_show_end_date.strftime('%b %Y')}" if org_show_end_date.present? %></span>
<i class="fa-solid fa-circle-info text-gray-400 text-xs"></i>
</p>
<div class="hidden group-hover:block absolute z-50 left-0 bottom-full mb-1 bg-blue-100 text-gray-700 text-xs rounded-lg shadow-lg ring-1 ring-blue-200 p-3 w-64 whitespace-normal">
<% if org_earliest_affiliation.present? %>
<p>Earliest start date from affiliation records.</p>
<% if @organization.start_date.present? && @organization.start_date.beginning_of_month != org_earliest_affiliation.beginning_of_month %>
<p class="mt-2 text-gray-500"><i class="fa-solid fa-circle-info mr-1"></i>Organization start_date (<%= @organization.start_date.strftime('%b %Y') %>) differs from earliest affiliation.</p>
<% end %>
<% else %>
<p>Using organization start_date.<br><br><span class="text-amber-700 font-medium">⚠ No affiliations have a start date. Add a start date to an affiliation to use it here instead.</span></p>
<% end %>
<% if org_show_aff_ended %>
<p class="mt-2 text-amber-700 font-medium">⚠ All affiliations have ended.</p>
<% end %>
</div>
</div>
<% end %>
<!-- Contact info -->
<div class="mt-3 flex flex-wrap justify-center md:justify-start items-center gap-x-4 gap-y-2 text-sm">
Expand Down
Loading