+
<% flash.each do |type, message| %>
<% next if message.blank? %>
- <% alert = type.to_s == "alert" %>
-
">
-
" aria-hidden="true">
-
<%= message %>
-
×
-
+ <%= render "shared/toast", type: type, message: message %>
<% end %>
diff --git a/app/views/shared/_toast.html.erb b/app/views/shared/_toast.html.erb
new file mode 100644
index 0000000..2e5e36f
--- /dev/null
+++ b/app/views/shared/_toast.html.erb
@@ -0,0 +1,12 @@
+<%# locals: (type:, message:) %>
+<% alert = type.to_s == "alert" %>
+
">
+
" aria-hidden="true">
+
<%= message %>
+
×
+
diff --git a/app/views/users/stories/show.html.erb b/app/views/users/stories/show.html.erb
new file mode 100644
index 0000000..988ca52
--- /dev/null
+++ b/app/views/users/stories/show.html.erb
@@ -0,0 +1,39 @@
+
+
+
About you
+
+ Share your background, family, and experiences that shaped who you are. This creates a
+ personal record for your family and provides context for your advisors.
+
+
+ <%= form_with url: users_story_path, method: :patch, scope: :user, class: "contents",
+ data: { controller: "debounced-form", action: "input->debounced-form#submit" } do |form| %>
+ <% if @user.errors.any? %>
+
+
+ <% @user.errors.full_messages.each do |message| %>
+ <%= message %>
+ <% end %>
+
+
+ <% end %>
+
+
+ <%= form.label :background, "Background", class: "text-sm font-medium text-ink" %>
+ <%= form.text_area :background, rows: 5, placeholder: "Where you're from, your work, the path that brought you here.", class: "block shadow-sm rounded-md border border-line bg-surface px-3 py-2 mt-2 w-full focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/10" %>
+
+
+
+ <%= form.label :family, "Family", class: "text-sm font-medium text-ink" %>
+ <%= form.text_area :family, rows: 5, placeholder: "The people who matter most and the family story you want to carry forward.", class: "block shadow-sm rounded-md border border-line bg-surface px-3 py-2 mt-2 w-full focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/10" %>
+
+
+
+ <%= form.label :formative_experiences, "Experiences that shaped you", class: "text-sm font-medium text-ink" %>
+ <%= form.text_area :formative_experiences, rows: 5, placeholder: "Moments, people, or turning points that shaped your values.", class: "block shadow-sm rounded-md border border-line bg-surface px-3 py-2 mt-2 w-full focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/10" %>
+
+
+
Your answers save automatically as you type.
+ <% end %>
+
+
diff --git a/config/routes.rb b/config/routes.rb
index d2b9189..4df8432 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,6 +1,7 @@
Rails.application.routes.draw do
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
resource :home
+ resource :dashboard, only: :show
resources :passwords, param: :token
resource :registration, only: %i[ new create ]
resource :email_confirmation, only: %i[ new create show ]
@@ -11,6 +12,7 @@
get :confirm, on: :member
end
resource :profile, only: %i[ show update ]
+ resource :story, only: %i[ show update ]
end
resource :session
diff --git a/db/migrate/20260702195446_add_about_you_to_users.rb b/db/migrate/20260702195446_add_about_you_to_users.rb
new file mode 100644
index 0000000..3eda697
--- /dev/null
+++ b/db/migrate/20260702195446_add_about_you_to_users.rb
@@ -0,0 +1,7 @@
+class AddAboutYouToUsers < ActiveRecord::Migration[8.1]
+ def change
+ add_column :users, :background, :text
+ add_column :users, :family, :text
+ add_column :users, :formative_experiences, :text
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index e6f925a..6b49201 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[8.1].define(version: 2026_07_01_000000) do
+ActiveRecord::Schema[8.1].define(version: 2026_07_02_195446) do
create_table "active_storage_attachments", force: :cascade do |t|
t.string "name", null: false
t.string "record_type", null: false
@@ -126,6 +126,9 @@
t.string "unconfirmed_email"
t.boolean "super_admin", default: false, null: false
t.string "name"
+ t.text "background"
+ t.text "family"
+ t.text "formative_experiences"
t.index ["email_address"], name: "index_users_on_email_address", unique: true
end
diff --git a/test/controllers/dashboards_controller_test.rb b/test/controllers/dashboards_controller_test.rb
new file mode 100644
index 0000000..b58e23c
--- /dev/null
+++ b/test/controllers/dashboards_controller_test.rb
@@ -0,0 +1,29 @@
+require "test_helper"
+
+class DashboardsControllerTest < ActionDispatch::IntegrationTest
+ test "requires authentication" do
+ get dashboard_url
+ assert_redirected_to new_session_path
+ end
+
+ test "renders the workspace dashboard for a signed-in member" do
+ sign_in_as(users(:one))
+
+ get dashboard_url
+
+ assert_response :success
+ assert_select "h1", "Welcome to your workspace"
+ assert_select "a[href=?]", users_story_path, text: "About you"
+ assert_select "a[href=?]", scenarios_path, text: "Explore options"
+ # The nav brand/logo links back to the dashboard for signed-in users.
+ assert_select "nav a[href=?]", dashboard_path
+ end
+
+ test "a signed-in non-member is redirected to the apex" do
+ sign_in_as(users(:two)) # member of boston, not arlington
+
+ get dashboard_url
+
+ assert_redirected_to root_url(subdomain: false)
+ end
+end
diff --git a/test/controllers/email_confirmations_controller_test.rb b/test/controllers/email_confirmations_controller_test.rb
index b29859f..571396c 100644
--- a/test/controllers/email_confirmations_controller_test.rb
+++ b/test/controllers/email_confirmations_controller_test.rb
@@ -43,7 +43,7 @@ class EmailConfirmationsControllerTest < ActionDispatch::IntegrationTest
get email_confirmation_path(token: token)
assert @user.reload.confirmed?
- assert_redirected_to root_path
+ assert_redirected_to dashboard_path
assert cookies[:session_id]
end
diff --git a/test/controllers/home_controller_test.rb b/test/controllers/home_controller_test.rb
index efb9bd5..47e3f57 100644
--- a/test/controllers/home_controller_test.rb
+++ b/test/controllers/home_controller_test.rb
@@ -8,16 +8,17 @@ class HomeControllerTest < ActionDispatch::IntegrationTest
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"
+ # The nav brand/logo links to the marketing home when signed out.
+ assert_select "nav a[href=?]", root_path
end
- test "show offers explore options and sign out when signed in" do
+ test "show still renders the marketing page for a signed-in member" do
sign_in_as(users(:one))
get root_url
assert_response :success
- assert_select "a[href=?]", scenarios_path, text: "Explore options"
- assert_select "button", text: "Sign out"
+ assert_select "h1", "Your Legacy Starts Here"
end
test "a signed-in non-member is redirected to the apex" do
diff --git a/test/controllers/magic_links_controller_test.rb b/test/controllers/magic_links_controller_test.rb
index 5cab762..44dadd6 100644
--- a/test/controllers/magic_links_controller_test.rb
+++ b/test/controllers/magic_links_controller_test.rb
@@ -58,7 +58,7 @@ class MagicLinksControllerTest < ActionDispatch::IntegrationTest
get magic_link_path(token: token)
- assert_redirected_to root_url
+ assert_redirected_to dashboard_url
assert cookies[:session_id]
end
@@ -70,7 +70,7 @@ class MagicLinksControllerTest < ActionDispatch::IntegrationTest
get magic_link_path(token: token)
assert user.reload.confirmed?
- assert_redirected_to root_url
+ assert_redirected_to dashboard_url
assert cookies[:session_id]
end
diff --git a/test/controllers/sessions_controller_test.rb b/test/controllers/sessions_controller_test.rb
index cfece16..f4da66d 100644
--- a/test/controllers/sessions_controller_test.rb
+++ b/test/controllers/sessions_controller_test.rb
@@ -13,7 +13,7 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest
test "create with valid credentials" do
post session_path, params: { email_address: @user.email_address, password: "password" }
- assert_redirected_to root_url
+ assert_redirected_to dashboard_url
assert cookies[:session_id]
end
diff --git a/test/controllers/users/stories_controller_test.rb b/test/controllers/users/stories_controller_test.rb
new file mode 100644
index 0000000..55c470e
--- /dev/null
+++ b/test/controllers/users/stories_controller_test.rb
@@ -0,0 +1,64 @@
+require "test_helper"
+
+class Users::StoriesControllerTest < ActionDispatch::IntegrationTest
+ test "show requires authentication" do
+ get users_story_path
+ assert_redirected_to new_session_path
+ end
+
+ test "show for an authenticated user" do
+ sign_in_as users(:one)
+ get users_story_path
+ assert_response :success
+ assert_select "h1", "About you"
+ end
+
+ test "a user can save their background, family, and formative experiences" do
+ user = users(:one)
+ sign_in_as user
+
+ patch users_story_path, params: {
+ user: {
+ background: "Grew up in Arlington.",
+ family: "Two kids.",
+ formative_experiences: "A mentor changed my path."
+ }
+ }
+
+ assert_redirected_to users_story_path
+ user.reload
+ assert_equal "Grew up in Arlington.", user.background
+ assert_equal "Two kids.", user.family
+ assert_equal "A mentor changed my path.", user.formative_experiences
+ end
+
+ test "autosave (turbo_stream) persists and responds with a Saved toast without redirecting" do
+ user = users(:one)
+ sign_in_as user
+
+ patch users_story_path, params: { user: { background: "Autosaved text." } }, as: :turbo_stream
+
+ assert_response :success
+ assert_equal "text/vnd.turbo-stream.html", response.media_type
+ # Appends a toast into the flash container rather than navigating away.
+ assert_match %r{turbo-stream action="append" target="flash"}, response.body
+ assert_match "Saved.", response.body
+ assert_equal "Autosaved text.", user.reload.background
+ end
+
+ test "a user can clear their story fields" do
+ user = users(:one)
+ user.update!(background: "Something", family: "Someone", formative_experiences: "A moment")
+ sign_in_as user
+
+ patch users_story_path, params: {
+ user: { background: "", family: "", formative_experiences: "" }
+ }
+
+ assert_redirected_to users_story_path
+ user.reload
+ assert_equal "", user.background
+ assert_equal "", user.family
+ assert_equal "", user.formative_experiences
+ end
+end