Status: ✅ Complete
Priority: Critical (Pre-MVP)
Estimated Effort: 2-3 weeks
Prerequisites: Phase 4 Complete
Completed: January 2026Related Documents:
- ../../ai/AI_03_USER_ROLES_AND_PERMISSIONS.md — Verification status matrix
- ../../ai/AI_05_LANGUAGE_AND_GLOSSARY.md — Tone and language patterns
- ../../development/DESIGN-GUIDELINES.md — Visual and UX patterns
- ../../development/LANGUAGE_STYLE_GUIDE.md — Copy guidelines
Completed: January 2026
Tests: 2372 runs, 0 failures, 0 errors
Cp::LandingController with champion/city stats/app/views/cp/landing/show.html.erb with full design:
unauthenticated :cp_champion root → landing pagetest/controllers/cp/landing_controller_test.rbrequire_champion_verified! guardscommunity_connection_copy, community_private_copy| File | Purpose |
|——|———|
| app/controllers/cp/landing_controller.rb | Landing page controller |
| app/views/cp/landing/show.html.erb | Landing page view |
| test/controllers/cp/landing_controller_test.rb | Landing page tests |
| File | Changes |
|——|———|
| config/routes.rb | Added unauthenticated root for landing page |
| app/controllers/cp/community_suggestions_controller.rb | Added verification guard |
| app/controllers/cp/dashboard_controller.rb | Conditional community suggestions loading |
| app/views/cp/dashboard/show.html.erb | Verification-aware community widgets |
| app/views/layouts/champions/_header.html.erb | Access strategy navigation |
| app/views/layouts/champions/_mobile_nav.html.erb | Access strategy navigation |
| app/views/cp/communities/show.html.erb | Overhauled eligibility messaging |
| app/helpers/cp/communities_helper.rb | Added standardized copy helpers |
| docs/planning/champion-portal/development/LANGUAGE_STYLE_GUIDE.md | Community eligibility copy rules |
Date: January 2026
Strategic Decision: Rather than locking email-verified users out of all content (which creates a dead end and leads to disengagement), we implement a “browse vs act” framework:
Rationale:
Updated Access Rules by Feature:
| Feature | Browse (email_verified) | Act (champion_verified only) |
|---|---|---|
| Communities | ✅ Index/Show (stats, descriptions) | ❌ Join/Leave |
| Discussions | ⚠️ Titles only (content gated with “After verification” note) | ❌ Post, Comment, React |
| Events | ✅ Index/Show (details, location, time) | ❌ RSVP |
| News | ✅ Full access | (no actions) |
| Directory | ❌ Locked | ✅ Full access |
| Messages | ❌ Locked | ✅ Full access |
| Community Suggestions | ❌ Hidden | ✅ Full access |
Date: January 2026
What Was Fixed:
require_champion_verified! to CommunitySuggestionsControllerload_community_suggestions in dashboard controller to check verificationif current_cp_champion.status_champion_verified? && @pending_community_suggestions.any?_header.html.erb): Communities accessible to ALL authenticated users_mobile_nav.html.erb): Communities accessible, Directory/Messages lockedFiles Changed:
app/controllers/cp/community_suggestions_controller.rbapp/controllers/cp/dashboard_controller.rbapp/views/cp/dashboard/show.html.erbapp/views/layouts/champions/_header.html.erbapp/views/layouts/champions/_mobile_nav.html.erbtest/controllers/cp/community_suggestions_controller_test.rbTests: 15 runs, 0 failures (community_suggestions_controller_test.rb)
This document ensures every screen, message, CTA, and interaction is intentionally designed for the user’s current verification state. The Champion Portal currently has gaps where users see content or can take actions that don’t match their access level.
Today’s implementation has inconsistencies:
Create three distinct, intentional experiences:
From LANGUAGE_STYLE_GUIDE.md:
| State | Auth Status | Verification | Can Access |
|---|---|---|---|
| Anonymous | Not logged in | N/A | Public landing, event/community previews, login/signup |
| Email Verified | Logged in | email_verified |
Dashboard (limited), own profile edit, Help, Settings |
| Champion Verified | Logged in | champion_verified |
Full portal: Directory, Communities, Messaging, Events, Discussions |
# In controllers/views:
# Anonymous
!cp_champion_signed_in?
# Email Verified (awaiting verification)
cp_champion_signed_in? && current_cp_champion.status_email_verified? && !current_cp_champion.status_champion_verified?
# Champion Verified
cp_champion_signed_in? && current_cp_champion.status_champion_verified?
Anonymous ─────────────────> Email Verified ─────────────────> Champion Verified
(signup + email (staff links to
confirmation) alumni record)
Anonymous visitors should:
| Screen | Current | Desired |
|---|---|---|
| Root URL | Login form | Inspiring landing page THEN login option |
| Public Events | Event details visible | ✅ Good (public events work) |
| Public Communities | Community preview visible | ✅ Good (community invite flow works) |
| Directory | Redirects to login | Soft teaser → “Log in to find your people” |
Route: alumnichampions.com (root, unauthenticated)
URL Change:
# config/routes.rb — update root for anonymous users
unauthenticated :cp_champion do
root to: 'cp/landing#show', as: :cp_unauthenticated_root
end
Hero Section:
┌─────────────────────────────────────────────────────────────────┐
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ [Belmont campus or alumni photo] │ │
│ │ │ │
│ │ "Your people are waiting." │ │
│ │ │ │
│ │ Connect with fellow Bruins in your city. │ │
│ │ Host events. Share stories. Belong. │ │
│ │ │ │
│ │ [Sign Up — It's Free] [I Already Have an Account] │ │
│ │ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
What Is a Champion Section:
┌─────────────────────────────────────────────────────────────────┐
│ What is an Alumni Champion? │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Champions are Belmont alumni who stay connected and help │
│ others do the same. Whether you're hosting a coffee meetup, │
│ mentoring a student, or just catching up with fellow Bruins, │
│ being a Champion means showing up for your community. │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 🤝 │ │ 🎉 │ │ 💬 │ │
│ │ Connect │ │ Host Events │ │ Share │ │
│ │ Find alumni │ │ Bring │ │ Stories │ │
│ │ in your │ │ Bruins │ │ Celebrate │ │
│ │ city │ │ together │ │ wins │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Social Proof Section:
┌─────────────────────────────────────────────────────────────────┐
│ "[Count] Champions in [Count] cities" │
│ │
│ [Nashville] [Atlanta] [Dallas] [Chicago] [Los Angeles] ... │
│ │
│ Maybe show a few actual Champion photos (with permission) │
│ │
└─────────────────────────────────────────────────────────────────┘
Final CTA:
┌─────────────────────────────────────────────────────────────────┐
│ │
│ Ready to find your people? │
│ │
│ [Join the Community] │
│ │
│ Already have an account? [Log In] │
│ │
└─────────────────────────────────────────────────────────────────┘
Implemented in Phase 3.9. These are key “first impression” pages that Champions share externally.
When existing Champions share links via social media, email, or messaging, anonymous visitors land on these pages. They must be inspiring, informative, and drive signups — not feel like locked-out screens.
Reference Implementation:
app/views/cp/shared/_public_cta_box.html.erb — Reusable warm CTA componentapp/helpers/meta_tags_helper.rb — Rich Open Graph/Twitter Card previewsstore_return_path / stored_return_path — Post-login redirect to original pageRoute: alumnichampions.com/events/:id (when show_globally: true or in public community)
What Anonymous Visitors See:
CTA Pattern:
┌─────────────────────────────────────────────────────────────────┐
│ 🎟️ Interested in attending? │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Join Alumni Champions to RSVP and connect with attendees. │
│ │
│ [Sign Up — It's Free] [Already have an account? Log in] │
│ │
└─────────────────────────────────────────────────────────────────┘
Post-Login Flow: After signup/login, user is returned to this event page (via stored_return_path).
Route: alumnichampions.com/communities/:slug (public communities or via ?ref= invite link)
What Anonymous Visitors See:
Public Stats Pattern (from controller):
@public_stats = {
member_count: @total_member_count,
post_count: @community.board_posts.visible.count,
event_count: @community.events.published.upcoming.count
}
CTA Pattern (Invite Flow):
┌─────────────────────────────────────────────────────────────────┐
│ [Photo] [Inviter Name] invited you to this community! │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Join to connect with fellow Bruins who share your interests. │
│ │
│ [Sign Up to Join] │
│ │
│ Already a member? [Log in] │
│ │
└─────────────────────────────────────────────────────────────────┘
Post-Login Flow: After signup/login, user is returned to this community page and can immediately join.
Route: alumnichampions.com/discussions/:id (posts in public communities only)
What Anonymous Visitors See:
CTA Pattern:
┌─────────────────────────────────────────────────────────────────┐
│ 💬 Want to join the conversation? │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Sign up to comment, react, and connect with fellow Bruins. │
│ │
│ [Sign Up — It's Free] [Log in] │
│ │
└─────────────────────────────────────────────────────────────────┘
All public pages include rich previews via MetaTagsHelper:
<% set_event_meta_tags(@event) %> # Events
<% set_community_meta_tags(@community, @inviter) %> # Communities
<% set_discussion_meta_tags(@post) %> # Discussions
These generate Open Graph (Facebook/LinkedIn) and Twitter Card meta tags for beautiful link previews.
| Page/Feature | Allowed? | Behavior |
|---|---|---|
| Landing page | ✅ Yes | Full access |
| Login/Signup | ✅ Yes | Full access |
| Public event (via link) | ✅ Yes | View event + “Join to RSVP” CTA |
| Public community (via link/invite) | ✅ Yes | View stats + “Join” CTA |
| Public discussion (via link) | ✅ Yes | View post + “Join to comment” CTA |
| Directory | ❌ No | Redirect to login with message |
| Dashboard | ❌ No | Redirect to login |
| Messages | ❌ No | Redirect to login |
| Discussions (index) | ❌ No | Redirect to login |
Primary messaging theme: “Belonging starts here”
| Context | Copy |
|---|---|
| Hero headline | “Your people are waiting.” |
| Subhead | “Connect with fellow Bruins in your city.” |
| Value prop | “Find alumni. Host events. Share stories. Belong.” |
| CTA (primary) | “Join the Community” or “Sign Up — It’s Free” |
| CTA (secondary) | “I Already Have an Account” → Log In |
| Directory tease | “Log in to find Belmont alumni near you” |
| Event tease | “Join to RSVP and connect with attendees” |
Email-verified users should:
⚠️ UPDATED January 2026: Desired state updated to implement “Browse vs Act” framework.
| Screen/Feature | Current | Desired |
|---|---|---|
| Dashboard | Shows community suggestions | Hide community suggestions, show browse options |
| Community Suggestions | Visible and actionable | Hidden entirely |
| Directory | May be accessible | Locked with explanation |
| Messages | May be accessible | Locked with explanation |
| Communities | May show browse option | ✅ Browse OK — can see index/show, NO join/leave |
| Discussions | May be accessible | ⚠️ Titles only — content gated with verification teaser |
| Events | May be accessible | ✅ Browse OK — can see all, NO RSVP |
| News | ✅ Accessible | ✅ Keep (read-only by nature) |
| Profile Edit | ✅ Accessible | ✅ Keep (encourage completion) |
| Settings | ✅ Accessible | ✅ Keep |
| Help/FAQ | ✅ Accessible | ✅ Keep |
The dashboard should feel welcoming and productive, not locked-down.
Hero:
┌─────────────────────────────────────────────────────────────────┐
│ │
│ Welcome, [FirstName]! 👋 │
│ │
│ You're almost there. Our team is reviewing your account │
│ to confirm your Belmont connection. │
│ │
│ While you wait, complete your profile so other Champions │
│ can find you once you're verified. │
│ │
│ [ Complete Your Profile ] │
│ │
└─────────────────────────────────────────────────────────────────┘
Visible Sections:
Hidden Sections:
“What’s Coming” Teaser Card:
┌─────────────────────────────────────────────────────────────────┐
│ 🔐 After Verification │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Once verified, you'll be able to: │
│ │
│ ✓ Find Champions in your city │
│ ✓ Join communities based on your interests │
│ ✓ Message fellow alumni │
│ ✓ RSVP to events │
│ │
│ Verification usually takes 1-2 business days. │
│ │
└─────────────────────────────────────────────────────────────────┘
⚠️ UPDATED January 2026: This section has been updated to implement the “Browse vs Act” framework. See Completion Summary for rationale.
| Page/Feature | Allowed? | Behavior |
|---|---|---|
| Dashboard | ✅ Limited | Verification-aware version |
| Profile Edit | ✅ Yes | Encourage completion |
| Settings | ✅ Yes | Full access |
| Help/FAQ | ✅ Yes | Full access |
| Directory | ❌ No | “Available after verification” |
| Communities Index | ✅ Browse | View communities, stats, descriptions — NO join/leave |
| Community Show | ✅ Browse | View community details — NO join/leave |
| Community Suggestions | ❌ No | Hide entirely (not just disable) |
| Messages | ❌ No | “Available after verification” |
| Discussions Index | ✅ Browse | View titles + excerpt only — NOT full content |
| Discussion Show | ⚠️ Teaser | Show title + “Unlock full discussion after verification” |
| Discussion Actions | ❌ No | No post/comment/react until verified |
| Events Index | ✅ Browse | View all events (details visible) |
| Event Show | ✅ Browse | View event details — NO RSVP |
| News | ✅ Yes | Full access (read-only by nature) |
When a user tries to access a locked feature, show a warm, informative message — not a cold error.
Pattern:
┌─────────────────────────────────────────────────────────────────┐
│ │
│ 🔐 Directory access coming soon │
│ │
│ Once your account is verified, you'll be able to browse │
│ the full directory and find Champions near you. │
│ │
│ Verification usually takes 1-2 business days. │
│ │
│ In the meantime, [complete your profile] so others can │
│ find you. │
│ │
└─────────────────────────────────────────────────────────────────┘
Copy by Feature:
| Feature | Message |
|---|---|
| Directory | “Directory access coming soon. Once verified, you’ll find Champions near you.” |
| Communities | “Communities unlock after verification. You’ll be able to join based on your college, interests, and location.” |
| Messages | “Direct messaging opens after verification. You’ll be able to connect one-on-one with fellow Champions.” |
| Discussions | “Community discussions are available after verification. You’ll be able to join conversations with your communities.” |
Primary messaging theme: “Almost there — let’s get you ready”
| Context | Copy |
|---|---|
| Dashboard greeting | “Welcome, [Name]! 👋 You’re almost there.” |
| Verification status | “Our team is reviewing your account to confirm your Belmont connection.” |
| Timeline | “Verification usually takes 1-2 business days.” |
| Profile CTA | “Complete your profile while you wait” |
| Locked feature | “Available after verification” |
| What’s coming | “Once verified, you’ll be able to…” |
Verified Champions should:
Hero (Returning User):
┌─────────────────────────────────────────────────────────────────┐
│ │
│ Good [morning/afternoon/evening], [FirstName]! 👋 │
│ │
│ You're part of a community of [X] Champions in [Y] cities. │
│ │
└─────────────────────────────────────────────────────────────────┘
Full Dashboard Sections:
| Page/Feature | Allowed? | Notes |
|---|---|---|
| Dashboard | ✅ Full | All widgets visible |
| Directory | ✅ Full | Search, filter, view profiles |
| Communities | ✅ Full | Browse, join, participate |
| Community Suggestions | ✅ Full | Accept/decline |
| Messages | ✅ Full | Send, receive, mute |
| Discussions | ✅ Full | Post, comment, react |
| Events | ✅ Full | View, RSVP, create (if enabled) |
| Profile | ✅ Full | Edit all sections |
| Settings | ✅ Full | All settings |
For verified Champions who haven’t engaged recently, show gentle nudges:
Community Join Nudge:
"You haven't joined any communities yet. Find one that fits →"
Profile Completion Nudge:
"Your profile is 75% complete. Add a photo so others can recognize you."
First Message Nudge:
"Say hello! Send your first message to a fellow Champion."
Every empty state should encourage action, not leave the user stranded.
| Section | Empty State |
|---|---|
| Messages | “Your inbox is empty. Find a Champion and start a conversation!” |
| My Communities | “You haven’t joined any communities yet. Browse communities →” |
| Discussions | “No discussions yet. Join a community to see what people are talking about.” |
| District Preview | “No Champions in your area yet. Could you be the first?” |
Primary messaging theme: “Welcome home — your community is here”
| Context | Copy |
|---|---|
| Dashboard greeting | “Good [time], [Name]! 👋” |
| Community context | “You’re part of [X] Champions in [Y] cities” |
| Empty inbox | “Your inbox is empty — start a conversation!” |
| Empty communities | “Join a community to connect with Champions who share your interests” |
| Profile nudge | “Add a photo so others can recognize you” |
This is a sub-state of Champion Verified. The user has full portal access but is viewing a community they haven’t joined yet. This is a critical moment — we want them to feel welcomed and understand the value of joining.
Scenarios:
When a verified Champion views a community they’re not a member of, they see:
What’s Visible:
Eligibility Status Messaging:
| Situation | Icon | Message | CTA |
|---|---|---|---|
| Can join directly | ✅ Green checkmark | “You’re eligible to join!” + eligibility reason | [Join Community] |
| Pending request | ⏳ Clock | “Your request is being reviewed” + submission time | None (waiting) |
| Needs approval | 🔐 Lock | “This is a private community” + eligibility reason | [Request to Join] |
| Not eligible | ℹ️ Info | “This community has specific eligibility requirements” + reason | None |
Example: Can Join Directly
┌─────────────────────────────────────────────────────────────────┐
│ ✅ You're eligible to join! │
├─────────────────────────────────────────────────────────────────┤
│ │
│ This community is for Music Business majors. │
│ │
│ [Join Community] │
│ │
└─────────────────────────────────────────────────────────────────┘
Example: Private Community (Needs Approval)
┌─────────────────────────────────────────────────────────────────┐
│ 🔐 This is a private community │
├─────────────────────────────────────────────────────────────────┤
│ │
│ This community is for Nashville-area Champions. │
│ Request to join and we'll review your request. │
│ │
│ [Request to Join] │
│ │
└─────────────────────────────────────────────────────────────────┘
When a verified Champion follows an invite link (?ref=INVITE_CODE):
Enhanced Welcome Banner:
┌─────────────────────────────────────────────────────────────────┐
│ [Inviter Photo] [Inviter Name] invited you to this community! │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Join to connect with fellow Bruins who share your interests. │
│ │
│ [Join Community] │
│ │
└─────────────────────────────────────────────────────────────────┘
Tracking: Joining via invite link is tracked for the inviter’s metrics.
When a non-member tries to see gated content:
Members List:
┌─────────────────────────────────────────────────────────────────┐
│ 🔐 Join to see members │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Members are visible only to fellow members. │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Discussions (in sidebar): Show a teaser that encourages joining:
┌─────────────────────────────────────────────────────────────────┐
│ 💬 Discussions │
├─────────────────────────────────────────────────────────────────┤
│ │
│ This community has active discussions. │
│ Join to see what members are talking about. │
│ │
│ 📊 [X] posts this month │
│ │
└─────────────────────────────────────────────────────────────────┘
# app/controllers/cp/communities_controller.rb (existing)
# In show action for non-members:
@is_member = false
@is_community_leader = false
@can_join = can_join_community?(@community)
@requires_approval = @community.requires_approval?
@pending_request = current_cp_champion.join_requests.pending.find_by(community: @community)
@public_stats = {
member_count: @total_member_count,
post_count: @community.board_posts.visible.count,
event_count: @community.events.published.upcoming.count
}
Primary messaging theme: “You’re welcome here — join to get the full experience”
| Context | Copy |
|---|---|
| Eligible to join | “You’re eligible to join!” |
| Private community | “This is a private community — request to join” |
| Not eligible | “This community has specific eligibility requirements” |
| Members gated | “Members are visible only to fellow members” |
| Discussions gated | “Join to see what members are talking about” |
| Invite banner | “[Name] invited you to this community!” |
| Post-join welcome | “Welcome to [Community]! 🎉” |
Related: Phase 5.4 (Milestone Celebrations) depends on tracking which actions Champions have completed.
We track 55+ activity event types via Cp::ActivityEvent. This enables us to:
| Category | Event Type | What It Means | Milestone Prompt |
|---|---|---|---|
| Profile | profile_edit |
Edited profile at least once | “Complete your profile” |
| Profile | photo_uploaded |
Added a profile photo | “Add a photo so others recognize you” |
| Discovery | directory_search |
Used the directory search | “Find Champions near you” |
| Discovery | profile_view |
Viewed another Champion’s profile | “See who’s in your community” |
| Social | message_thread_started |
Started a conversation | “Say hello to someone” |
| Social | message_sent |
Sent at least one message | “Start a conversation” |
| Communities | community_joined |
Joined at least one community | “Join a community” |
| Communities | community_viewed |
Explored a community page | “Explore communities” |
| Events | event_viewed |
Viewed an event | “Check out upcoming events” |
| Events | event_rsvp_clicked |
RSVP’d to an event | “RSVP to an event” |
| Discussions | board_view |
Viewed a discussion board | “See what people are talking about” |
| Discussions | post_view |
Read a discussion post | “Read a discussion” |
| Discussions | post_created |
Started a discussion | “Start a conversation” |
| Discussions | comment_created |
Commented on a post | “Join the conversation” |
| Discussions | reaction_added |
Reacted to content | “Show some love” |
Similar to profile_completion_percentage, we can calculate engagement completion:
# Proposed: app/models/cp/champion.rb
def engagement_completion_percentage
milestones = [
activity_events.exists?(event_type: 'profile_edit'),
photo.attached?,
activity_events.exists?(event_type: 'directory_search'),
activity_events.exists?(event_type: 'message_thread_started'),
communities.exists?,
activity_events.exists?(event_type: 'event_viewed'),
activity_events.exists?(event_type: 'post_view'),
]
completed = milestones.count(true)
(completed.to_f / milestones.count * 100).round
end
Dashboard Nudges (for verified Champions):
Show a “Getting Started” or “Try This” card for Champions who haven’t hit key milestones:
┌─────────────────────────────────────────────────────────────────┐
│ ✨ Make the most of your Champion Portal │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ☐ Complete your profile │
│ ☐ Find Champions near you │
│ ☐ Join a community │
│ ☑ View an event (done!) │
│ ☐ Start a conversation │
│ │
└─────────────────────────────────────────────────────────────────┘
Inline Nudges:
Show contextual suggestions based on what’s missing:
| Missing Milestone | Where to Show | Nudge |
|---|---|---|
| No photo | Profile card | “Add a photo so others recognize you” |
| No communities | Dashboard | “Join a community to connect with Champions who share your interests” |
| No messages sent | Dashboard messages section | “Your inbox is empty — start a conversation!” |
| No event views | Dashboard events section | “See what’s happening near you →” |
| No reactions | Discussion view | “💡 React to show you were here” |
When a Champion completes a milestone for the first time, celebrate:
| First-Time Action | Celebration |
|---|---|
| First message sent | “🎉 You sent your first message!” |
| First community joined | “🎉 Welcome to [Community]!” |
| First event RSVP | “🎉 See you at [Event]!” |
| First discussion post | “🎉 Your first post is live!” |
| First reaction given | “💙 Spread the love!” |
| All milestones complete | “🏆 You’ve unlocked the full Champion experience!” |
Full list from app/models/cp/activity_event.rb:
Profile & Account:
login, profile_edit, email_updated, photo_uploaded, settings_updated, notification_preferences_updated, google_connected, google_disconnected, password_changed, deletion_requested, tooltip_dismissedDiscovery:
directory_search, profile_view, news_viewedMessaging:
message_thread_started, message_sent, message_reaction, message_read, thread_muted, thread_unmutedCommunities:
community_joined, community_left, community_viewed, communities_viewedEvents:
event_viewed, event_rsvp_clickedDiscussions:
board_view, post_view, post_created, post_edited, post_deleted, comment_created, comment_edited, comment_deleted, reaction_added, reaction_removedSocial:
invite_sent, invite_acceptedModeration:
content_reported, content_hidden, content_blocked, post_pinned, post_unpinned, post_locked, post_unlocked, moderation_escalatedAdmin:
join_request_approved, join_request_declinedThe navigation should adapt based on user state:
Anonymous:
Email Verified:
Champion Verified:
# app/controllers/cp/base_controller.rb
# Helper to check verification state for views
helper_method :can_access_verified_features?
def can_access_verified_features?
cp_champion_signed_in? && current_cp_champion.status_champion_verified?
end
# Redirect with warm messaging
def require_champion_verified_with_message!(feature_name)
return if current_cp_champion.status_champion_verified?
message = verification_message_for(feature_name)
redirect_to cp_dashboard_path, alert: message
end
private
def verification_message_for(feature)
case feature
when :directory
"Directory access opens after verification — usually 1-2 business days."
when :communities
"Communities unlock after your account is verified."
when :messages
"Direct messaging is available once you're verified."
else
"This feature is available after verification."
end
end
All flash messages should use warm language:
| Type | Style | Example |
|---|---|---|
| Success | Celebratory | “Welcome to Nashville Champions! 🎉” |
| Info | Helpful | “Verification usually takes 1-2 business days.” |
| Warning | Gentle | “Your session will expire in 5 minutes.” |
| Error | Supportive | “Something went wrong — we’re looking into it.” |
All state-specific UI must work on mobile:
Location: Dashboard view + CommunitySuggestionsController
Problem: Email-verified users can see the “Suggested Communities” widget and interact with accept/decline buttons.
Fix Applied (January 2026):
require_champion_verified!Location: Navigation partial
Problem: Full navigation may show communities, messages, etc. to email-verified users.
Fix Applied (January 2026):
status_champion_verified? blockLocation: Various controllers
Problem: Email-verified users may be able to access features by typing URLs directly.
Status: Need to audit all Champion Portal controllers:
| Controller | Required Guard | Status |
|---|---|---|
Cp::DirectoryController |
require_champion_verified! |
⏳ Verify |
Cp::CommunitiesController |
require_champion_verified! (except public show) |
⏳ Verify |
Cp::MessageThreadsController |
require_champion_verified! |
⏳ Verify |
Cp::DiscussionsController |
require_champion_verified! |
⏳ Verify |
Cp::CommunitySuggestionsController |
require_champion_verified! |
✅ Fixed |
Cp::LandingController with show actionapp/views/cp/landing/show.html.erb with landing page designload_community_suggestions to check verification statusrequire_champion_verified! to CommunitySuggestionsControllerbefore_action guards| File | Purpose |
|---|---|
app/controllers/cp/landing_controller.rb |
Landing page for anonymous users |
app/views/cp/landing/show.html.erb |
Landing page view |
app/views/cp/shared/_verification_pending.html.erb |
Reusable “waiting for verification” partial |
app/views/cp/shared/_locked_feature.html.erb |
Reusable “feature locked” partial |
| File | Changes |
|---|---|
config/routes.rb |
Update unauthenticated root |
app/controllers/cp/dashboard_controller.rb |
Verification-aware widget loading |
app/controllers/cp/community_suggestions_controller.rb |
Add verification guard |
app/views/cp/dashboard/show.html.erb |
Verification-aware sections |
app/views/layouts/cp/_navigation.html.erb |
State-aware navigation |
| Various controllers | Audit before_action guards |
This document should be updated as implementation progresses. Mark checklist items as complete and note any deviations from the plan.