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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,6 @@
# Ignore key files for decrypting credentials and more.
/config/credentials/*.key

# Ignore personal local Claude instructions.
/CLAUDE.local.md

4 changes: 2 additions & 2 deletions app/models/organization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ class Organization < ApplicationRecord
length: { maximum: 63 },
exclusion: { in: %w[ www mail ftp admin api app root ], message: "is reserved" }
validates :website,
format: { with: URI::DEFAULT_PARSER.make_regexp(%w[ http https ]), message: "must be a valid URL" },
allow_blank: true
presence: true,
format: { with: URI::DEFAULT_PARSER.make_regexp(%w[ http https ]), message: "must be a valid URL" }
end
2 changes: 2 additions & 0 deletions app/views/home/sections/_contact_button.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<%= link_to "Contact us", Current.organization.website, target: "_blank", rel: "noopener",
class: "inline-block rounded-md border border-line bg-surface px-6 py-2.5 font-medium text-ink hover:bg-canvas transition" %>
37 changes: 37 additions & 0 deletions app/views/home/sections/_faq.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<% faqs = [
{ q: "What is legacy giving?",
a: "Legacy giving is a way to support the causes and communities you care about through a planned gift, often as part of your estate or long-term financial plans." },
{ q: "Do I need to have a plan before I start?",
a: "Not at all. You can explore ideas and learn about your options without any commitment. There's no requirement to decide anything right away." },
{ q: "Is there any cost or obligation to explore?",
a: "No. Exploring possibilities and starting a conversation is free and carries no obligation to follow through." },
{ q: "Can I change my mind later?",
a: "Yes. Your priorities can evolve over time, and you can revisit and update your reflections whenever you'd like." },
{ q: "How do I get help from the team?",
a: "You can reach out anytime to ask questions or discuss ideas. The team is here to help you navigate the possibilities at your own pace." }
] %>

<section class="bg-surface-soft border-t border-line-soft">
<div class="mx-auto max-w-3xl w-full px-4 py-20">
<h2 class="text-center font-serif font-medium text-4xl tracking-tight text-ink">Frequently asked questions</h2>

<div class="mt-12 space-y-3">
<% faqs.each do |faq| %>
<details class="group rounded-xl border border-line bg-surface px-5 py-4">
<summary class="flex cursor-pointer list-none items-center justify-between font-medium text-ink">
<%= faq[:q] %>
<span class="ml-4 text-ink-faint transition group-open:rotate-45">+</span>
</summary>
<p class="mt-3 text-ink-soft"><%= faq[:a] %></p>
</details>
<% end %>
</div>

<div class="mt-12 text-center">
<p class="font-serif text-2xl text-ink">Still have questions?</p>
<div class="mt-6">
<%= render "home/sections/contact_button" %>
</div>
</div>
</div>
</section>
22 changes: 22 additions & 0 deletions app/views/home/sections/_hero.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<section class="bg-canvas">
<div class="mx-auto max-w-3xl w-full px-4 py-20 text-center">
<h1 class="font-serif font-medium text-5xl tracking-tight text-ink">Your Legacy Starts Here</h1>
<p class="mt-4 font-serif text-2xl text-ink">You don't need to have everything figured out to begin</p>
<p class="mx-auto mt-6 max-w-md text-ink-soft">
Explore ideas, discover possibilities, and connect with
<%= Current.organization.name %> whenever you're ready.
</p>

<div class="mt-12 flex flex-wrap items-center justify-center gap-4">
<% if authenticated? %>
<%= link_to "Explore options", scenarios_path,
class: "rounded-md bg-accent px-6 py-2.5 font-medium text-white hover:bg-[#444] transition" %>
<% else %>
<%= link_to "Sign up", new_registration_path,
class: "rounded-md bg-accent px-6 py-2.5 font-medium text-white hover:bg-[#444] transition" %>
<%= link_to "Log in", new_session_path,
class: "rounded-md border border-line bg-surface px-6 py-2.5 font-medium text-ink hover:bg-canvas transition" %>
<% end %>
</div>
</div>
</section>
34 changes: 34 additions & 0 deletions app/views/home/sections/_pillars.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<% pillars = [
{ title: "Explore possibilities", body: "Learn about different approaches to legacy giving and the impact they can create." },
{ title: "Reflect on What Matters", body: "Capture ideas, values, and priorities as they evolve over time." },
{ title: "Learn Without Commitment", body: "Compare options and explore possibilities before making decisions." },
{ title: "Connect With the Advisor", body: "Ask questions, discuss ideas, and get support whenever you need it." }
] %>

<section class="bg-surface-soft border-t border-line-soft">
<div class="mx-auto max-w-5xl w-full px-4 py-20">
<div class="text-center">
<h2 class="font-serif font-medium text-4xl tracking-tight text-ink">Explore, Reflect, and Plan at Your Own Pace</h2>
<p class="mx-auto mt-6 max-w-2xl text-ink-soft">You don't need to have everything figured out to begin.</p>
<p class="mx-auto mt-4 max-w-2xl text-ink-soft">
Explore ideas, learn about different options, and capture what matters most to you. When you're
ready or whenever questions come up, the <%= Current.organization.name %> team is here to help
you navigate the possibilities.
</p>
</div>

<div class="mt-12 grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
<% pillars.each do |pillar| %>
<div class="rounded-2xl border border-line bg-surface p-5 shadow-sm">
<h3 class="font-serif text-lg font-medium text-ink"><%= pillar[:title] %></h3>
<p class="mt-2 text-sm text-ink-soft"><%= pillar[:body] %></p>
</div>
<% end %>
</div>

<div class="mt-12 text-center">
<%= link_to "Explore the workspace", get_started_path,
class: "inline-block rounded-md border border-line bg-surface px-6 py-2.5 font-medium text-ink hover:bg-canvas transition" %>
</div>
</div>
</section>
19 changes: 19 additions & 0 deletions app/views/home/sections/_questions.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<section class="bg-surface border-t border-line-soft">
<div class="mx-auto max-w-2xl w-full px-4 py-20 text-center">
<h2 class="font-serif font-medium text-4xl tracking-tight text-ink">Have Questions?</h2>

<p class="mt-6 text-ink-soft">
You don't need to have everything figured out before reaching out. There is no obligation
or commitment to start a conversation.
</p>
<p class="mt-4 text-ink-soft">
Whether you're simply curious about legacy giving, exploring possibilities, or considering
specific options, the <%= Current.organization.name %> team is happy to answer questions and
help you learn more.
</p>

<div class="mt-10">
<%= render "home/sections/contact_button" %>
</div>
</div>
</section>
53 changes: 53 additions & 0 deletions app/views/home/sections/_scenario_example.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<% segments = [
{ label: "Greatest Community Need", pct: 30, color: "#E07B39" },
{ label: "Program: Education", pct: 30, color: "#7C3AED" },
{ label: "Population: Youth", pct: 25, color: "#4FA89B" },
{ label: "Specific org", pct: 15, color: "#9B5DE5" }
] %>

<section class="bg-surface border-t border-line-soft">
<div class="mx-auto max-w-3xl w-full px-4 py-20">
<div class="text-center">
<h2 class="font-serif font-medium text-4xl tracking-tight text-ink">What Could Your Legacy Support?</h2>
<p class="mx-auto mt-4 max-w-xl text-ink-soft">
This is only an example. Your legacy plan can reflect the causes, communities,
and organizations that matter most to you.
</p>
</div>

<figure class="mt-12 rounded-2xl border border-line bg-surface-soft p-8">
<%# TODO: placeholder quote — replace with real donor quote before launch. Remove the red dashed wrapper too. %>
<div class="border-2 border-dashed border-red-500 p-4">
<p class="mb-2 text-center text-xs font-semibold uppercase tracking-wide text-red-500">Placeholder — replace this quote</p>
<blockquote class="text-center font-serif text-xl text-ink">
&ldquo;I wanted part of my gift to go where it's needed most, and part to something close to my heart.&rdquo;
</blockquote>
<figcaption class="mt-3 text-center text-sm text-ink-soft">&mdash; Sarah, donor</figcaption>
</div>

<div class="mt-8 flex h-4 w-full overflow-hidden rounded-full">
<% segments.each do |segment| %>
<div style="width: <%= segment[:pct] %>%; background-color: <%= segment[:color] %>;"></div>
<% end %>
</div>

<ul class="mt-6 space-y-3">
<% segments.each do |segment| %>
<li class="flex items-center justify-between text-sm">
<span class="flex items-center gap-2 text-ink">
<span class="inline-block h-2.5 w-2.5 rounded-full" style="background-color: <%= segment[:color] %>;"></span>
<%= segment[:label] %>
</span>
<span class="font-medium text-ink"><%= segment[:pct] %>%</span>
</li>
<% end %>
</ul>
</figure>

<div class="mt-12 text-center">
<p class="font-serif text-2xl text-ink">Curious what your own priorities might look like?</p>
<%= link_to "Explore your own scenario", get_started_path,
class: "mt-6 inline-block rounded-md border border-line bg-surface px-6 py-2.5 font-medium text-ink hover:bg-canvas transition" %>
</div>
</div>
</section>
17 changes: 17 additions & 0 deletions app/views/home/sections/_stories.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<section class="bg-surface-soft border-t border-line-soft">
<div class="mx-auto max-w-5xl w-full px-4 py-20">
<h2 class="font-serif font-medium text-4xl tracking-tight text-ink">Legacy stories</h2>

<div class="mt-10 grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
<% 3.times do %>
<div class="aspect-[4/3] rounded-xl border border-line-soft bg-line-soft"></div>
<% end %>
</div>

<div class="mt-16 text-center">
<p class="font-serif text-2xl text-ink">Inspired by these stories?</p>
<%= link_to "Start your reflection", get_started_path,
class: "mt-6 inline-block rounded-md border border-line bg-surface px-6 py-2.5 font-medium text-ink hover:bg-canvas transition" %>
</div>
</div>
</section>
27 changes: 7 additions & 20 deletions app/views/home/show.html.erb
Original file line number Diff line number Diff line change
@@ -1,21 +1,8 @@
<div class="mx-auto max-w-xl w-full px-4 py-16 text-center">
<% if authenticated? %>
<h1 class="font-serif font-medium text-4xl tracking-tight text-ink">
Hello, <span class="text-brand"><%= Current.user.email_address %></span>
</h1>
<p class="mt-3 text-ink-soft">You're signed in to <%= Current.organization.name %>.</p>
<% get_started_path = authenticated? ? scenarios_path : new_registration_path %>

<div class="mt-8 flex items-center justify-center gap-4">
<%= link_to "Explore options", scenarios_path, class: "rounded-md px-3.5 py-2.5 bg-accent hover:bg-[#444] text-white font-medium inline-block transition" %>
<%= button_to "Sign out", session_path, method: :delete, class: "rounded-md px-3.5 py-2.5 border border-line bg-surface hover:bg-canvas text-ink-soft font-medium cursor-pointer transition" %>
</div>
<% else %>
<h1 class="font-serif font-medium text-4xl tracking-tight text-ink">Welcome to <%= Current.organization.name %></h1>
<p class="mt-3 text-ink-soft">Sign in to your account or create a new one to get started.</p>

<div class="mt-8 flex items-center justify-center gap-4">
<%= link_to "Sign in", new_session_path, class: "rounded-md px-3.5 py-2.5 bg-accent hover:bg-[#444] text-white font-medium inline-block transition" %>
<%= link_to "Sign up", new_registration_path, class: "rounded-md px-3.5 py-2.5 border border-line bg-surface hover:bg-canvas text-ink-soft font-medium inline-block transition" %>
</div>
<% end %>
</div>
<%= render "home/sections/hero", get_started_path: get_started_path %>
<%= render "home/sections/pillars", get_started_path: get_started_path %>
<%= render "home/sections/scenario_example", get_started_path: get_started_path %>
<%= render "home/sections/stories", get_started_path: get_started_path %>
<%= render "home/sections/questions" %>
<%= render "home/sections/faq" %>
28 changes: 15 additions & 13 deletions app/views/layouts/application.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,22 @@
<%= link_to (Current.organization&.name || "Community Foundations"), root_path,
class: "font-serif text-lg tracking-tight text-ink hover:text-ink" %>

<% if Rails.env.development? || authenticated? %>
<div class="ml-auto flex items-center gap-4 text-sm">
<% if Rails.env.development? %>
<%= link_to "Mailer previews", "/rails/mailers", class: "text-ink-soft hover:text-ink" %>
<div class="ml-auto flex items-center gap-4 text-sm">
<% if Rails.env.development? %>
<%= link_to "Mailer previews", "/rails/mailers", class: "text-ink-soft hover:text-ink" %>
<% end %>
<% if authenticated? %>
<% if Current.user&.admin_of?(Current.organization) %>
<%= link_to "Members", organization_memberships_path, class: "text-ink-soft hover:text-ink" %>
<% end %>
<% if authenticated? %>
<% if Current.user&.admin_of?(Current.organization) %>
<%= link_to "Members", organization_memberships_path, class: "text-ink-soft hover:text-ink" %>
<% end %>
<%= link_to "Settings", users_password_path, class: "text-ink-soft hover:text-ink" %>
<%= button_to "Sign out", session_path, method: :delete, class: "text-ink-soft hover:text-ink cursor-pointer" %>
<% end %>
</div>
<% end %>
<%= link_to "Settings", users_password_path, class: "text-ink-soft hover:text-ink" %>
<%= button_to "Sign out", session_path, method: :delete, class: "text-ink-soft hover:text-ink cursor-pointer" %>
<% elsif Current.organization %>
<%= link_to "Log in", new_session_path, class: "text-ink-soft hover:text-ink" %>
<%= link_to "Explore the legacy builder platform", new_registration_path,
class: "rounded-md bg-accent px-3.5 py-2 font-medium text-white hover:bg-[#444] transition" %>
<% end %>
</div>
</nav>

<%= render "shared/flash" %>
Expand Down
10 changes: 5 additions & 5 deletions test/controllers/home_controller_test.rb
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
require "test_helper"

class HomeControllerTest < ActionDispatch::IntegrationTest
test "show on a tenant subdomain offers sign in and sign up when signed out" do
test "show on a tenant subdomain offers sign up and log in when signed out" do
host! "arlington.localhost"
get root_url

assert_response :success
assert_select "h1", /Welcome to #{Regexp.escape(organizations(:arlington).name)}/
assert_select "a[href=?]", new_session_path, text: "Sign in"
assert_select "h1", "Your Legacy Starts Here"
assert_select "a[href=?]", new_registration_path, text: "Sign up"
assert_select "a[href=?]", new_session_path, text: "Log in"
end

test "show greets a member by email and offers sign out when signed in" do
test "show offers explore options and sign out when signed in" do
host! "arlington.localhost"
sign_in_as(users(:one))

get root_url

assert_response :success
assert_select "h1", /Hello, #{Regexp.escape(users(:one).email_address)}/
assert_select "a[href=?]", scenarios_path, text: "Explore options"
assert_select "button", text: "Sign out"
end

Expand Down
9 changes: 7 additions & 2 deletions test/models/organization_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,13 @@ class OrganizationTest < ActiveSupport::TestCase
assert_includes org.errors[:subdomain], "is reserved"
end

test "rejects an invalid website but allows a blank one" do
assert Organization.new(name: "Test", subdomain: "test").valid?
test "requires a website" do
org = Organization.new(name: "Test", subdomain: "test")
assert_not org.valid?
assert_includes org.errors[:website], "can't be blank"
end

test "rejects an invalid website" do
invalid = Organization.new(name: "Test", subdomain: "test2", website: "not a url")
assert_not invalid.valid?
assert_includes invalid.errors[:website], "must be a valid URL"
Expand Down
Loading