Champion Portal Development Sub-Phase 1.10
Estimated Effort: 3–4 weeks
Focus: Admin-managed news/announcements system for Champion Portal dashboardPrerequisites: Phase 1.9 complete (dashboard placeholder exists)
Related Documents:
- ../../JOBS-TO-BE-DONE.md — Job C5: Stay in the Loop, Job C9: Feel Like I Belong
- ../../development/DESIGN-GUIDELINES.md — Visual design standards
- ../phase-3/README.md — Discussion Boards (separate from this feature)
Phase 1.10 implements the Community News & Announcements system — staff-curated content that gives Champions a reason to return to the dashboard. This replaces the hardcoded placeholder cards in the dashboard’s “From the Belmont Community” section.
After Phase 1.10, the system supports:
Key Distinction: This is curated content (staff/CLC authored), distinct from Phase 3’s discussion boards (Champion-generated posts with comments/threading).
From 1.9-pre-beta-polish.md §1.9.4:
The dashboard currently lacks “freshness” — nothing new to see on return visits. A News/Posts section is critical for engagement.
Without fresh content:
| Job | How 1.10 Addresses It |
|---|---|
| C5: Stay in the Loop | Fresh content on every visit; regional news keeps Champions informed |
| C9: Feel Like I Belong | Alumni spotlights, community activity reinforce belonging |
| L3: Communicate with My City | CLCs can post regional announcements |
Decisions made during interview (January 2026):
| Decision | Choice | Rationale |
|---|---|---|
| Content model | Single unified model | Flexible fields adapt to content type (story, photo, announcement) |
| External links | Support both link-out AND full content | Some posts link to belmont.edu, others have full article |
| Images | Multiple images with carousel | Hero image on dashboard card, full carousel on show page |
| Admin location | Split by scope | Staff: Lookup Portal (global), CLCs: Champion Portal (regional) |
| Who can manage | Portal Admin+ and CLCs | Staff for global, CLCs for their region |
| Publish workflow | Draft + Publish | Create drafts, publish when ready |
| Dashboard display | 3 posts + “View all” | Clean display with link to full listing |
| Ordering | Pinned + Published date | Admin can pin important posts; otherwise newest first |
| Audience targeting | Global OR by Region | Posts can target all Champions or specific region(s) |
| Rich text | ActionText (Trix) for both excerpt and full content | Basic formatting: paragraphs, bold, italic, links |
| Tagging | Colleges, majors, affinities, champions | College UI now; major/affinity UI deferred; champion tagging for spotlights |
| Analytics | Full tracking | Views, clicks, likes per Champion |
| Regional display | Same section with badge | Regional posts show in “From the Belmont Community” with distinctive badge |
| Champion mentions | Both manual links and structured tags | Admin can link to profiles in content AND tag featured champions |
| Friendly URLs | /news/:id/:slug format |
ID for matching, slug for readability |
| Likes | Simple like button | Heart/thumbs up with count, tracks who liked |
| Image carousel | Show page only | Dashboard shows hero image, show page has carousel |
| Feature | Phase 1.10 News | Phase 3 Boards |
|---|---|---|
| Authors | Staff, CLCs | Champions |
| Content type | Curated announcements | Community discussion |
| Comments | No | Yes |
| Threading | No | Yes (flat or threaded) |
| Moderation | Admin-only creation | Champion posts + moderation |
| Dashboard display | “From the Belmont Community” | Regional board feed (future) |
These are separate models — discussion board posts may eventually be “elevated” to dashboard, but the news system is distinct.
Regional posts appear in the same “From the Belmont Community” section as global posts, but with a distinctive badge:
Future Vision: The “Your [Nashville] Community” section on the dashboard is envisioned as a unified hub for all regional content:
For now, regional news shows in “From the Belmont Community” with a badge. Future phases may consolidate regional content into the community section.
| Sub-Phase | Name | Est. Time | Status |
|---|---|---|---|
| 1.10.1 | Database & Models | 1–2 days | ✅ Complete |
| 1.10.2 | Lookup Portal Admin (Staff/Global) | 2–3 days | ✅ Complete |
| 1.10.3 | Champion Portal Display | 2–3 days | ✅ Complete |
| 1.10.4 | Champion Portal CLC Admin (Regional) | 2–3 days | ⏸️ Deferred |
| 1.10.5 | Likes & Analytics | 1–2 days | ⏸️ Deferred |
| 1.10.6 | Testing & Polish | 1–2 days | ⏸️ Deferred |
Completed: January 5, 2026
Goal: Create the database structure for news posts, images, likes, and analytics.
What Was Implemented:
cp_news_posts table migration with ActionText support, polymorphic author, counter cachescp_news_post_likes table migration with counter cachecp_news_post_views table migration with source tracking and durationcp_news_post_regions join table migration for regional targetingcp_clc_assignments table migration for CLC role trackingCp::NewsPost model with full associations, validations, scopes (published, draft, global, regional, visible_to, for_region, pinned_first)Cp::NewsPostLike model with counter cache updatesCp::NewsPostView model with source/referrer trackingCp::NewsPostRegion join modelCp::ClcAssignment model with champion CLC role methods (is_clc?, clc_for_region?, clc_region_names)Deferred from 1.10.1 (per interview):
cp_news_post_images table (using ActiveStorage hero_image for now; carousel deferred)cp_news_post_colleges join table (UI deferred)cp_news_post_majors join table (UI deferred)cp_news_post_affinities join table (UI deferred)cp_news_post_champions join table (featured champions - UI deferred)Key Implementation Notes:
prefix: true on enums → status_published?, scope_global? predicate methodsgenerate_unique_slug callbackUser (staff) and Cp::Champion (CLC)likes_count, views_count, clicks_count for performancecolumn_id: <%= ActiveRecord::FixtureSet.identify(:label) %> when model has explicit foreign_key: optionAcceptance Test:
# In rails console:
post = Cp::NewsPost.create!(
title: "Alumni Spotlight: Sarah Chen",
excerpt: "'18 grad launches tech startup...",
status: :published,
scope: :global,
author: User.find_by(email: "admin@example.com")
)
post.images.attach(io: File.open("test.jpg"), filename: "test.jpg")
post.published? # => true
post.visible_to?(champion) # => true
Completed: January 5, 2026
Goal: Staff can create, edit, publish, and manage global news posts.
What Was Implemented:
Settings::NewsPostsController in Lookup Portal with full CRUD + publish/unpublish actionsresources :news_posts, except: [:show] with publish/unpublish member routesAuthorization:
portal_admin and admin roles can access (via ensure_portal_admin!)skip_before_action :ensure_admin! to allow portal_admin accessKey Implementation Notes:
to_param returning slug → controller finds by find_by!(slug: params[:id])Cp::NewsPost model class nameinverse_of: :news_post on associations to allow building region associations before saveslug.present? to handle validation failure re-renderDeferred from 1.10.2 (per interview):
Files Created:
app/controllers/settings/news_posts_controller.rbapp/views/settings/news_posts/index.html.erbapp/views/settings/news_posts/new.html.erbapp/views/settings/news_posts/edit.html.erbapp/views/settings/news_posts/_form.html.erbtest/controllers/settings/news_posts_controller_test.rbFiles Modified:
config/routes.rb — Added news_posts resource in settings namespaceapp/views/settings/_sidebar.html.erb — Added Champion Portal sectionapp/models/cp/news_post.rb — Added inverse_of: :news_post to news_post_regionsapp/models/cp/news_post_region.rb — Added inverse_of: :news_post_regions to news_post associationAcceptance Test:
Completed: January 5, 2026
Goal: Champions see news posts on dashboard and can view full posts.
What Was Implemented:
Cp::NewsController with index and show actionsCp::NewsPost records via load_news_posts methodsample_news_posts helper with real data from visible_to(champion) scope/news) — all published posts visible to Champion (12 per page, paginated)/news/:slug) with:
hero_image method)_post_card.html.erb) for reuse on dashboard and indexvisible_to scopeviews_count, records news_viewed activity eventDeferred from 1.10.3 (per interview):
Phase 1.10.3 Additions (Jan 5, 2026):
image_carousel_controller.js)
Files Created:
app/controllers/cp/news_controller.rbapp/views/cp/news/index.html.erbapp/views/cp/news/show.html.erbapp/views/cp/news/_post_card.html.erbtest/controllers/cp/news_controller_test.rbFiles Modified:
config/routes.rb — Added news routes in Cp moduleapp/controllers/cp/dashboard_controller.rb — Added load_news_posts methodapp/views/cp/dashboard/show.html.erb — Uses real @news_posts variableapp/models/cp/activity_event.rb — Added news_viewed to EVENT_TYPESAcceptance Test:
Status: ⏸️ DEFERRED (January 2026)
Reason: CLC administration is being reconsidered as part of the Community Foundation (Phase 1.12). CLCs may become “Community Leadership Council” members associated with Communities rather than Regions. Deferring to align with new community model.
Target: Phase 1.12+ (after Community Foundation establishes the CLC-Community relationship)
Original Goal: CLCs can create regional announcements for their region’s Champions.
Deliverables (Deferred):
Cp::Admin::NewsPostsController in Champion Portal/admin/news_posts on champions subdomainAuthorization:
CLC Detection:
# Champion is CLC if they have CLC role assignment
# (Assuming CLC role tracking exists or will be added)
current_cp_champion.clc_regions # => [Region<Nashville>, Region<Atlanta>]
Note: CLC role assignment may need to be added if not already implemented. Check existing CLC tracking before implementation.
Acceptance Test:
Status: ⏸️ DEFERRED (January 2026)
Reason: Core news functionality is complete. Likes and analytics are nice-to-have engagement features that can be added later. Prioritizing Community Foundation (1.12-1.14) and Events (1.11) over engagement metrics.
Target: Future (Phase 2+)
Original Goal: Champions can like posts; staff can see engagement analytics.
Deliverables (Deferred):
Cp::NewsPostLikesController for like actionsnews_post_liked and news_post_viewed to activity eventsLike Behavior:
Analytics Dashboard:
┌─────────────────────────────────────────────────────────────────────────┐
│ News Post Analytics │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Post: "Alumni Spotlight: Sarah Chen" │
│ Published: Jan 5, 2026 • Global │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Views │ │ Unique │ │ Likes │ │ Clicks │ │
│ │ 234 │ │ 187 │ │ 42 │ │ 28 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ Engagement by Region: │
│ Nashville ████████████████ 45% │
│ Atlanta ██████████ 25% │
│ Dallas ██████ 15% │
│ Other ██████ 15% │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Acceptance Test:
Status: ⏸️ DEFERRED (January 2026)
Reason: Core functionality has test coverage from 1.10.1-1.10.3. Additional polish and edge case handling can happen alongside other features.
Target: Ongoing (as part of regular development)
Original Goal: Full test coverage, edge cases, and UI polish.
Deliverables (Deferred):
cp_news_posts Tablecreate_table :cp_news_posts do |t|
# Content
t.string :title, null: false
t.string :slug, null: false # URL-friendly title (auto-generated)
# Note: excerpt uses ActionText (has_rich_text :excerpt)
# Note: full_content uses ActionText (has_rich_text :full_content)
t.string :external_link # Optional URL to external content
# Metadata
t.string :status, default: 'draft' # draft, published, archived
t.string :scope, default: 'global' # global, regional
t.boolean :pinned, default: false
t.datetime :published_at
# Author tracking
t.string :author_type # 'User' (staff) or 'Cp::Champion' (CLC)
t.bigint :author_id
# Analytics (denormalized for performance)
t.integer :views_count, default: 0
t.integer :likes_count, default: 0
t.integer :clicks_count, default: 0 # External link clicks
t.timestamps
end
add_index :cp_news_posts, [:status, :published_at]
add_index :cp_news_posts, [:scope, :status]
add_index :cp_news_posts, [:author_type, :author_id]
add_index :cp_news_posts, :pinned
add_index :cp_news_posts, :slug
cp_news_post_images Tablecreate_table :cp_news_post_images do |t|
t.references :cp_news_post, null: false, foreign_key: true
t.integer :position, default: 0 # For carousel ordering
t.string :alt_text # Accessibility
t.timestamps
end
add_index :cp_news_post_images, [:cp_news_post_id, :position]
# Images stored via ActiveStorage
# has_one_attached :image on Cp::NewsPostImage
cp_news_post_regions Join Tablecreate_table :cp_news_post_regions do |t|
t.references :cp_news_post, null: false, foreign_key: true
t.references :region, null: false, foreign_key: true
t.timestamps
end
add_index :cp_news_post_regions, [:cp_news_post_id, :region_id], unique: true
cp_news_post_colleges Join Tablecreate_table :cp_news_post_colleges do |t|
t.references :cp_news_post, null: false, foreign_key: true
t.references :college, null: false, foreign_key: true
t.timestamps
end
add_index :cp_news_post_colleges, [:cp_news_post_id, :college_id], unique: true
cp_news_post_likes Tablecreate_table :cp_news_post_likes do |t|
t.references :cp_news_post, null: false, foreign_key: true
t.references :cp_champion, null: false, foreign_key: { to_table: :cp_champions }
t.timestamps
end
add_index :cp_news_post_likes, [:cp_news_post_id, :cp_champion_id], unique: true
add_index :cp_news_post_likes, :cp_champion_id
cp_news_post_views Tablecreate_table :cp_news_post_views do |t|
t.references :cp_news_post, null: false, foreign_key: true
t.references :cp_champion, null: false, foreign_key: { to_table: :cp_champions }
t.string :source # 'dashboard', 'index', 'direct'
t.timestamps
end
add_index :cp_news_post_views, [:cp_news_post_id, :cp_champion_id]
add_index :cp_news_post_views, :created_at
cp_news_post_majors Join Tablecreate_table :cp_news_post_majors do |t|
t.references :cp_news_post, null: false, foreign_key: true
t.references :major, null: false, foreign_key: true
t.timestamps
end
add_index :cp_news_post_majors, [:cp_news_post_id, :major_id], unique: true
Note: Table created now; admin UI deferred to future phase.
cp_news_post_affinities Join Tablecreate_table :cp_news_post_affinities do |t|
t.references :cp_news_post, null: false, foreign_key: true
t.references :affinity, null: false, foreign_key: true
t.timestamps
end
add_index :cp_news_post_affinities, [:cp_news_post_id, :affinity_id], unique: true
Note: Table created now; admin UI deferred to future phase.
cp_news_post_champions Join Table (Featured Champions)create_table :cp_news_post_champions do |t|
t.references :cp_news_post, null: false, foreign_key: true
t.references :cp_champion, null: false, foreign_key: { to_table: :cp_champions }
t.integer :position, default: 0 # Display order
t.timestamps
end
add_index :cp_news_post_champions, [:cp_news_post_id, :cp_champion_id], unique: true
add_index :cp_news_post_champions, :cp_champion_id
Purpose: Track Champions featured/mentioned in a post. Enables:
┌─────────────────────────────────────────────────────────────────────────┐
│ 📰 From the Belmont Community [View all →] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ [IMAGE] │ │ [IMAGE] │ │ [IMAGE] │ │
│ │ │ │ │ │ │ │
│ ├─────────────────────┤ ├─────────────────────┤ ├─────────────────────┤ │
│ │ 📌 Alumni Spotlight │ │ 🏙️ FOR NASHVILLE │ │ Campus News │ │
│ │ Sarah Chen '18 │ │ Champions Meetup │ │ Bell Tower... │ │
│ │ │ │ 15 Champions... │ │ │ │
│ │ ❤️ 42 │ │ ❤️ 28 │ │ ❤️ 15 │ │
│ │ Read more → │ │ Read more → │ │ Read more → │ │
│ └─────────────────────┘ └─────────────────────┘ └─────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Dashboard Card Rules:
/news)Display: 10 posts per page with standard pagination (not “Load more”)
URL format: /news/:id/:slug (e.g., /news/42/alumni-spotlight-sarah-chen)
┌─────────────────────────────────────────────────────────────────────────┐
│ Community News │
│ Stories, spotlights, and announcements from the Belmont community │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ [IMAGE] │ │
│ │ 📌 PINNED │ │
│ │ Alumni Spotlight: Sarah Chen '18 Launches Tech Startup │ │
│ │ After graduating with a degree in Computer Science, Sarah moved... │ │
│ │ ❤️ 42 • Jan 5, 2026 [Read more →] │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ [IMAGE] │ │
│ │ 🏙️ FOR NASHVILLE │ │
│ │ Nashville Champions Meetup Recap │ │
│ │ 15 Champions gathered at Belmont's annual homecoming celebration... │ │
│ │ ❤️ 28 • Jan 3, 2026 [Read more →] │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ [IMAGE] │ │
│ │ Campus News: Bell Tower Renovation Complete │ │
│ │ The iconic Belmont Bell Tower has been fully restored... │ │
│ │ ❤️ 15 • Dec 28, 2025 [Read more →] │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ [← Previous] Page 1 of 3 [Next →] │
│ │
└─────────────────────────────────────────────────────────────────────────┘
/news/:id/:slug)URL format: /news/42/alumni-spotlight-sarah-chen
id (slug can change without breaking URL)┌─────────────────────────────────────────────────────────────────────────┐
│ ← Back to News │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ [IMAGE CAROUSEL] │ │
│ │ │ │
│ │ ○ ● ○ ○ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ Alumni Spotlight: Sarah Chen '18 Launches Tech Startup │
│ ───────────────────────────────────────────────────────────────────── │
│ Jan 5, 2026 • ❤️ 42 likes │
│ │
│ [❤️ Like] [🔗 Copy Link] [↗️ Visit belmont.edu] │
│ │
│ ───────────────────────────────────────────────────────────────────── │
│ │
│ After graduating with a degree in Computer Science from Belmont's │
│ College of Science and Mathematics, Sarah Chen moved to Austin, TX │
│ where she founded GreenThread, a sustainable fashion marketplace. │
│ │
│ "My time at Belmont taught me to think entrepreneurially," Sarah says. │
│ "The close-knit community gave me the confidence to take risks." │
│ │
│ GreenThread has since raised $2.5M in seed funding and now employs │
│ 15 people, including two other Belmont alumni. │
│ │
│ ───────────────────────────────────────────────────────────────────── │
│ │
│ 👤 Featured in this post │
│ ┌───────────┐ ┌───────────┐ │
│ │ [Photo] │ │ [Photo] │ │
│ │ Sarah Chen│ │ Mike Torres│ │
│ │ Austin, TX│ │ Austin, TX│ │
│ │ View → │ │ View → │ │
│ └───────────┘ └───────────┘ │
│ │
│ **Related to:** College of Science and Mathematics │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Champion Mentions:
┌─────────────────────────────────────────────────────────────────────────┐
│ Settings > News Posts > New Post │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Title * │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Alumni Spotlight: Sarah Chen '18 Launches Tech Startup │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ Excerpt / Caption * │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ After graduating with a degree in Computer Science, Sarah moved to │ │
│ │ Austin where she founded GreenThread, a sustainable fashion startup │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ ActionText editor (bold, italic, links) │
│ │
│ Full Content (optional) │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ [ActionText Editor] │ │
│ │ B I U 🔗 │ │
│ │ │ │
│ │ After graduating with a degree in Computer Science from Belmont's │ │
│ │ College of Science and Mathematics, Sarah Chen moved to Austin... │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ Leave blank if post should link to external content only │
│ │
│ External Link (optional) │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ https://www.belmont.edu/stories/sarah-chen-greenthread │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ If provided, "Read more" links here instead of show page │
│ │
│ ───────────────────────────────────────────────────────────────────── │
│ │
│ Images │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ [Image 1] [Image 2] [Image 3] [+ Add Image] │ │
│ │ Drag to reorder • First image is hero │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ───────────────────────────────────────────────────────────────────── │
│ │
│ Audience │
│ ○ Global (all Champions) │
│ ○ Specific Regions │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ ☑ Nashville ☐ Atlanta ☐ Dallas ☐ Los Angeles ☐ ... │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Related Colleges (optional) │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ ☐ College of Business ☑ College of Science & Math ☐ College of...│ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ Featured Champions (optional) │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ 🔍 Search champions... │ │
│ │ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Sarah Chen ×│ │ Mike Torres×│ │ │
│ │ └─────────────┘ └─────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ Tag Champions mentioned or featured in this post │
│ │
│ ───────────────────────────────────────────────────────────────────── │
│ │
│ Status: ○ Draft ○ Published │
│ │
│ ☐ Pin this post (appears first on dashboard) │
│ │
│ Published Date: [Jan 5, 2026 ▼] │
│ │
│ ───────────────────────────────────────────────────────────────────── │
│ │
│ [Save Draft] [Preview] [Publish] │
│ │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ CLC Tools > Regional Announcements │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Create announcements for your region's Champions. │
│ │
│ [+ New Announcement] │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Your Announcements │ │
│ ├─────────────────────────────────────────────────────────────────────┤ │
│ │ 📌 Nashville Champions Meetup — Jan 15! Published [Edit] │ │
│ │ Posted Jan 3, 2026 • 28 likes, 156 views │ │
│ ├─────────────────────────────────────────────────────────────────────┤ │
│ │ Welcome New Nashville Champions! Draft [Edit] │ │
│ │ Last edited Jan 4, 2026 │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ Note: Global announcements are managed by Belmont staff. │
│ │
└─────────────────────────────────────────────────────────────────────────┘
| Admin Location | Who | Can Manage |
|---|---|---|
Lookup Portal /settings/news_posts |
Portal Admin, Admin | Global posts, all regional posts |
Champion Portal /admin/news_posts |
CLCs | Their region’s posts only |
| Action | Admin | Portal Admin | CLC (Champion) | Champion |
|---|---|---|---|---|
| Create global post | ✅ | ✅ | ❌ | ❌ |
| Create regional post | ✅ | ✅ | Own region only | ❌ |
| Edit any post | ✅ | ✅ | ❌ | ❌ |
| Edit own regional post | ✅ | ✅ | ✅ | ❌ |
| Delete/archive post | ✅ | ✅ | Own region only | ❌ |
| View analytics | ✅ | ✅ | Own region only | ❌ |
| Like post | ✅ | ✅ | ✅ | ✅ |
| View posts | ✅ | ✅ | ✅ | ✅ |
Implementation Note: Need to verify how CLC role is currently tracked. Options:
current_cp_champion.clc_for_region?(region)
# New join table: cp_clc_assignments
# champion_id, region_id, assigned_at
Action: Check existing CLC tracking before 1.10.4 implementation.
Per BACKLOG §1.1, a future admin.alumnilookup.com could consolidate:
For now, the split approach (Option C) keeps related functions together:
| Area | Deliverables |
|---|---|
| Model | Single Cp::NewsPost with flexible fields |
| Images | Multiple images with carousel on show page |
| Admin (Lookup) | Full CRUD for global + all regional posts |
| Admin (Champion) | CLC CRUD for their region’s posts |
| Display | Dashboard section, index page, show page |
| Engagement | Like button with count |
| Analytics | Views, likes, clicks tracking and display |
| Targeting | Global or specific region(s) |
| Tagging | College, major, affinity, champion tags |
| Workflow | Draft → Published status |
| Feature | Reason | Target |
|---|---|---|
| Major/affinity tagging UI | Tables created; picker UI deferred | Phase 2+ |
| Comments on posts | This is curated content, not discussion | Phase 3 |
| Scheduled publishing | Nice-to-have, not MVP | Future |
| RSS feed import | Manual entry is fine for now | Future |
| Instagram API integration | Complexity, API limitations | Future |
| Carousel on dashboard cards | Start simple with hero image | Future |
| Push notifications for new posts | Requires notification infrastructure | Phase 4+ |
/news/:id/:slug| Metric | Target |
|---|---|
| Posts published (first month) | 10+ |
| Average likes per post | 10+ |
| Champions viewing news (% of active) | 50%+ |
| Dashboard return visits (with news) | +20% vs without |
# test/models/cp/news_post_test.rb
- validates title presence
- validates excerpt presence
- status enum works (draft, published, archived)
- scope enum works (global, regional)
- published scope returns only published posts
- visible_to?(champion) returns correct posts
- pinned posts sort first
- like_count increments correctly
- view_count increments correctly
# test/controllers/settings/news_posts_controller_test.rb
- index requires portal_admin
- create saves post as draft
- publish action changes status
- only portal_admin+ can access
# test/controllers/cp/news_controller_test.rb
- index shows published posts
- show increments view count
- respects scope filtering
- requires authenticated champion
# test/controllers/cp/admin/news_posts_controller_test.rb
- requires CLC role
- CLC can only create for their region
- CLC can edit own posts
- CLC cannot edit other region's posts
# test/integration/news_post_flow_test.rb
- staff creates and publishes post
- champion sees post on dashboard
- champion likes post
- like count updates
- CLC creates regional post
- regional post only visible to region
After completing Phase 1.10:
app/controllers/champions/roadmap_controller.rb with 1.10 statusdocs/features/champion_portal/NEWS_POSTS.mddocs/development/MODEL_RELATIONSHIPS.md with new modelsdocs/operations/MANAGING_NEWS_POSTS.md| Question | Answer | Notes |
|---|---|---|
| Content types | Single model | Flexible fields adapt to content type |
| External vs hosted | Both supported | external_link or full_content |
| Images | Optional with fallback | Placeholder if no image |
| Admin location | Split by scope | Staff: Lookup, CLC: Champion Portal |
| Who can manage | Portal Admin+ and CLCs | CLCs for regional only |
| Publish workflow | Draft + Publish | No scheduled publishing |
| Dashboard display | 3 + “View all” | No carousel on dashboard |
| Ordering | Pinned + date | Pinned first, then newest |
| Audience targeting | Global or regional | Multi-region supported |
| Discussion board relation | Separate models | News ≠ discussion posts |
| Rich text | ActionText (Trix) | Basic formatting only |
| Multiple images | Yes, carousel on show | Hero on dashboard card |
| Analytics | Full tracking | Views, likes, clicks |
| Likes | Yes | Simple like with count |
| Tagging | Colleges now, champion tags | Major/affinity UI deferred (tables ready) |
| URLs | Friendly slug format | /news/:id/:slug |
| Regional display | Same section with badge | “🏙️ FOR [REGION]” badge |
db/migrate/XXXXXX_create_cp_news_posts.rbdb/migrate/XXXXXX_create_cp_news_post_images.rbdb/migrate/XXXXXX_create_cp_news_post_regions.rbdb/migrate/XXXXXX_create_cp_news_post_colleges.rbdb/migrate/XXXXXX_create_cp_news_post_majors.rbdb/migrate/XXXXXX_create_cp_news_post_affinities.rbdb/migrate/XXXXXX_create_cp_news_post_champions.rbdb/migrate/XXXXXX_create_cp_news_post_likes.rbdb/migrate/XXXXXX_create_cp_news_post_views.rbapp/models/cp/news_post.rbapp/models/cp/news_post_image.rbapp/models/cp/news_post_champion.rbapp/models/cp/news_post_like.rbapp/models/cp/news_post_view.rbapp/controllers/settings/news_posts_controller.rbapp/controllers/cp/news_controller.rbapp/controllers/cp/news_post_likes_controller.rbapp/controllers/cp/admin/news_posts_controller.rbapp/views/settings/news_posts/ (index, new, edit, show)app/views/cp/news/ (index, show)app/views/cp/admin/news_posts/ (index, new, edit)app/javascript/controllers/like_button_controller.jsapp/javascript/controllers/image_carousel_controller.jstest/models/cp/news_post_test.rbtest/controllers/settings/news_posts_controller_test.rbtest/controllers/cp/news_controller_test.rbconfig/routes.rb — Add news routes for both portalsapp/views/cp/dashboard/show.html.erb — Replace placeholder with real dataapp/helpers/cp/dashboard_helper.rb — Remove sample_news_posts helperapp/controllers/cp/dashboard_controller.rb — Load news posts| Event Type | When Recorded | Metadata |
|---|---|---|
news_post_viewed |
Champion views show page | post_id, source |
news_post_liked |
Champion likes post | post_id |
news_post_unliked |
Champion unlikes post | post_id |
news_post_link_clicked |
Champion clicks external link | post_id, url |
Document created: January 4, 2026 Interview completed with user before spec creation