alumni_lookup

Champion Portal — Avoiding Duplication

⚠️ PLANNING DOCUMENT — Guidelines for Champion Portal development.

Related Documents:


Table of Contents

  1. Overview
  2. Known Overlap Areas
  3. Resolved: Data Ownership Summary
  4. Decision Framework
  5. Unification Patterns
  6. Example: Profile Photo Decision
  7. Pre-Implementation Checklist
  8. Existing Lookup Portal Features

1. Overview

As we build the Champion Portal alongside the existing Lookup Portal, we must continuously evaluate whether functionality, data, or features:

  1. Already exist and should be reused as-is
  2. Should be unified into a single source of truth
  3. Should be extracted into shared services/components
  4. Should replace legacy implementations
  5. Must coexist with clear ownership rules
  6. Should be configurable rather than hardcoded

This applies to code, data, features, and user-facing functionality — not just technical implementation.


2. Known Overlap Areas

2.1 HIGH PRIORITY — Must Resolve Before Building

Feature Lookup Portal Champion Portal Decision
Profile Photos Alumni photos (staff uploaded) Champion photos (self uploaded) DECIDED: Separate — different purposes
Contact Info alumni.email, alumni.phone cp_champions.email, cp_champions.phone DECIDED: Separate — no sync
Preferred Name alumni.pref_name N/A DECIDED: Shared on alumni, Champion can edit
Maiden Name alumni.maiden_name N/A DECIDED: Shared on alumni, Champion can edit
Affinities alumni_affinities (verified) cp_affinities (self-reported) Decide: How do these relate? Merge UI?
Location/Address alumni.city, alumni.state cp_champions.city, cp_champions.zip_code DECIDED: Separate — Champion owns their address

2.2 MEDIUM PRIORITY — Address at Phase Kickoff

Feature Lookup Portal Champion Portal Decision
Employment Info Not currently tracked cp_champions.employer, job_title Decide: Should this flow back to Alumni?
Notes/Bio alumni.prospect_notes (staff) Champion bio (self-written) DECIDED: Different purposes, keep separate
Email Preferences Not tracked Champion contact preferences N/A — Champion Portal only

2.3 LOWER PRIORITY — Defer Until Relevant

Feature Lookup Portal Champion Portal Decision
Activity History engagement_activities Portal activity logs DECIDED: Keep strictly separate
Event Attendance registrants (planned) cp_events (champion-created) Different concepts, keep separate

3. Resolved: Data Ownership Summary

Data Location Who Edits Notes
Name fields alumni Staff OR Champion (if verified) Shared source of truth
Photo (ID) alumni.photo Staff only For internal identification
Photo (profile) cp_champions.photo Champion only Self-representation, never shows staff photo
Contact (CRM) alumni.email/phone Staff only CRM data, not exposed to Champion Portal
Contact (portal) cp_champions.* Champion only For Champion Portal use
Address cp_champions.* Champion only Champion owns their address
Bio cp_champions.bio Champion only Different from prospect_notes
Prospect notes alumni.prospect_notes Staff only Internal notes, never shown

4. Decision Framework

When you encounter overlapping functionality, work through this decision tree:

┌─────────────────────────────────────────────────────────────────┐
│           Is this the SAME concept or DIFFERENT concepts?       │
└─────────────────────────────────────────────────────────────────┘
                              │
              ┌───────────────┴───────────────┐
              ▼                               ▼
        SAME CONCEPT                    DIFFERENT CONCEPTS
     (e.g., preferred name)           (e.g., staff photo vs profile photo)
              │                               │
              ▼                               ▼
┌─────────────────────────┐       ┌─────────────────────────┐
│  Who is the SOURCE OF   │       │  Document why separate  │
│  TRUTH?                 │       │  Keep in different      │
│                         │       │  tables/locations       │
│  □ Staff (Lookup)       │       └─────────────────────────┘
│  □ Champion (self)      │
│  □ Shared (complex)     │
└─────────────────────────┘
              │
              ▼
┌─────────────────────────────────────────────────────────────────┐
│                    UNIFICATION PATTERN                          │
├─────────────────────────────────────────────────────────────────┤
│  A. Single Location     — One table, both portals read/write    │
│  B. Primary + Override  — Lookup is default, Champion overrides │
│  C. Champion + Sync     — Champion owns, Staff can sync back    │
│  D. Staff + Display     — Staff owns, Champion can only view    │
└─────────────────────────────────────────────────────────────────┘

5. Unification Patterns

Pattern A: Single Location

Use when: Data should never diverge. One source of truth for everyone.

Example: Profile Photo (Alternative Approach)

┌─────────────┐
│   alumni    │
│  .photo     │◄──── Staff uploads (Lookup)
│  (attachment)│◄──── Champion uploads (Champion Portal)
└─────────────┘
       │
       ▼
   Both portals display the same photo

Implementation:


Pattern B: Primary + Override

Use when: Staff data is authoritative, but Champions can personalize.

Example: Preferred Name

┌─────────────┐     ┌──────────────┐
│   alumni    │     │ cp_champions │
│ .pref_name  │     │.preferred_   │
│ (from CRM)  │     │  name        │
└─────────────┘     └──────────────┘
       │                   │
       └────────┬──────────┘
                ▼
         Display Logic:
         champion.preferred_name || alumni.pref_name || alumni.first_name

Implementation:


Pattern C: Champion-Owned + Sync Back

Use when: Champions should control their own data, but Staff may want to sync verified info.

Example: Contact Information

┌──────────────┐         ┌─────────────┐
│ cp_champions │ ──────► │   alumni    │
│ .phone       │  Staff  │ .phone      │
│ .city        │  syncs  │ .city       │
│ .state       │  after  │ .state      │
└──────────────┘  review └─────────────┘
       │
       ▼
   Champion Portal shows cp_champions data
   Lookup Portal shows alumni data (may be older)

Implementation:


Pattern D: Staff-Owned + Display Only

Use when: Data must be authoritative and Champions shouldn’t change it.

Example: Degree Information

┌─────────────┐
│   degrees   │◄──── Staff imports from Registrar
└─────────────┘
       │
       ▼
   Champion Portal displays (read-only)
   "Your degrees on file..."

Implementation:


6. Example: Profile Photo Decision

Current State

Questions to Answer

  1. Is this the same concept?
    Decided: NO. Staff photo for ID purposes; Champion photo for self-representation.

  2. Who is the source of truth?
    Decided: SEPARATE. Each portal owns its own photo.

Decision: Champion-Owned (Separate)

# Champion owns their photo
class Cp::Champion < ApplicationRecord
  has_one_attached :photo  # Champion uploads here
end

# Staff photo stays on alumni record
class Alumni < ApplicationRecord
  has_one_attached :photo  # Staff uploads here (Lookup Portal)
end

Key Rule: Staff-uploaded photos NEVER appear in Champion Portal.


7. Pre-Implementation Checklist

Before starting any new feature, complete this checklist:

## Pre-Implementation Review for: [Feature Name]

### 1. Overlap Check
- [ ] Does this feature/data exist in Lookup Portal?
- [ ] Will both portals need to read this data?
- [ ] Will both portals need to write this data?
- [ ] Is this the SAME concept or DIFFERENT concepts?

### 2. If Same Concept — Unification Decision
- [ ] Who is the source of truth? (Staff / Champion / Shared)
- [ ] Which pattern applies?
  - [ ] A. Single Location
  - [ ] B. Primary + Override  
  - [ ] C. Champion + Sync
  - [ ] D. Staff + Display Only
- [ ] Document the decision in DECISIONS.md

### 3. If Different Concepts — Document Why
- [ ] Added comment explaining why separate
- [ ] Named clearly to distinguish (e.g., `prospect_notes` vs `bio`)

### 4. Code/Service Reuse
- [ ] Searched for existing implementation
- [ ] Evaluated extraction to shared service
- [ ] Checked for UI components to reuse

### 5. Data Architecture
- [ ] Table uses correct prefix (`cp_` for Champion Portal)
- [ ] Foreign keys properly defined
- [ ] Documented in data-model/README.md

8. Existing Lookup Portal Features

Before building Champion Portal equivalents, review what already exists:

8.1 Data/Models

Model Purpose Champion Portal Relevance
Alumni Core alumni record Links to Cp::Champion via BUID
Degree Academic records Display-only for Champions
Affinity Master affinity list Shared reference data
AlumniAffinity Staff-managed affiliations Separate from self-reported
EngagementActivity Tracked engagement NOT used by Champion Portal
ChampionSignup Interest form submissions Migrate to Cp::Champion?

8.2 Features

Feature Lookup Implementation Champion Portal Plan
Photo upload alumni.photo (ActiveStorage) ✅ DECIDED: Separate on cp_champions
CSV export Csv::*Exporter services Reuse pattern
Search pg_search Reuse or new?
Email sending ApplicationMailer Reuse

8.3 UI Components

Component Location Reusable?
Flash messages shared/_flash ✅ Yes
Pagination Various Extract to shared
Empty states Inline Extract to shared
Forms Various May need mobile variants

Document Purpose
DATA-ARCHITECTURE.md Data ownership between portals
DECISIONS.md Architectural decisions log
../../development/AGENTS.md Development guidelines