[리드관리 PR1] 리드 목록 + 퍼널 스코프
ID: 6f687bfb-eda2-46b1-8693-17c877cbd767
계획서: docs/features/admin_lead_management.md (§3, §6, §7-PR1)
목표: 운영자가 퍼널별(가입자/무료진단/유료진단/과정신청/수료자) 리드를 어드민에서 바로 조회. 읽기 전용, 위험 낮음. MVP 첫 단계.
작업:
- User 퍼널 스코프 추가(app/models/user.rb): with_completed_diagnosis, with_paid_diagnosis, free_diagnosis_only, course_graduates, marketing_subscribed, not_suppressed(※PR3 전까지는 EmailSuppression 미존재 → 이 스코프는 PR3에서 추가). User#funnel_stage(도달 최상위 단계 심볼) 구현.
- 인덱스 마이그레이션: diagnosis_sessions(is_report_purchased), diagnosis_sessions(user_id), expert_certificates(user_id) 확인/추가.
- Admin::Marketing::LeadsController#index — 기존 admin/users#index 패턴(검색/필터/정렬/_pagination) 복제. 컬럼: 이름/이메일, 퍼널 단계 배지, 진단 수, 마지막 활동, 수신동의. 필터: 퍼널 단계, 타겟, 수신동의, 가입/진단 기간, 검색(이름/이메일/전화). #show는 기존 admin_user_path로 링크.
- 라우트: namespace admin > namespace marketing > resources :leads, only: [index, show].
- 사이드바(_sidebar.html.erb) "리드 관리" 그룹 + nav_item 3개(leads/segments/campaigns) 추가 + ko.yml admin.nav 라벨 추가. (en/zh/vi 금지)
테스트(Minitest+fixtures):
- free_diagnosis_only: completed 보유 & 유료 0만 포함, 유료 보유자 제외.
- with_paid_diagnosis: is_report_purchased=true 세션 보유자만.
- course_graduates: completion 인증서 보유자만.
- funnel_stage: 수료자는 유료/무료 보유와 무관하게 graduate 반환.
- LeadsController#index: 비관리자 차단, 퍼널 필터 동작, 페이지네이션, ko 강제.
완료 기준: 어드민 사이드바 "리드 관리 > 리드"에서 퍼널별 필터·검색·페이지네이션 동작.
첨부 이미지
이미지 추가 (Ctrl+V로 붙여넣기 또는 클릭)
JPEG, PNG, GIF, WebP / 최대 10MB
활동 로그
-
Cclaude PR1 리드 목록 + 퍼널 스코프 TDD 구현 완료 (커밋 c65dcdf)
2026년 06월 10일 07:03:21
상세 보기
## 변경 사항
- `app/models/user.rb`: 퍼널 스코프/메서드 추가
- `FUNNEL_STAGES = %i[subscriber free paid applicant graduate]`
- fact 스코프: `marketing_subscribed`, `with_completed_diagnosis`, `with_paid_diagnosis`, `free_diagnosis_only`, `course_applicants`, `course_graduates`
- `at_funnel_stage(stage)`: 배지와 동일한 **상호배타 현재 단계** 필터
- `funnel_stage`(단건) / `self.funnel_stage_map(ids)`(배치, 목록 N+1 방지)
- `db/migrate/20260610065545_*`: `diagnosis_sessions.is_report_purchased` 부분 인덱스(`where is_report_purchased=true`, `algorithm: :concurrently`). migrate→rollback→migrate 3단계 검증 완료.
- `config/routes.rb`: `namespace :marketing { resources :leads, only: [:index] }`
- `app/controllers/admin/marketing/leads_controller.rb`: 검색(이름/이메일/전화)+퍼널 단계 필터+수신동의 필터+수동 페이지네이션. 페이지 단위 `funnel_stage_map`/진단수/last_active 일괄 조회.
- `app/views/admin/marketing/leads/index.html.erb`: 단계 칩 + 필터바 + 테이블(퍼널 배지/진단수/수신동의)
- `app/views/admin/shared/_sidebar.html.erb` + `_nav_item.html.erb`(funnel 아이콘) + `config/locales/ko.yml`(admin.nav.leads, admin.marketing.* — ko 전용)
- `docs/features/admin_lead_management.md`: 전체 기능 계획서## 설계 결정
- 스코프를 denorm(`first_diagnosis_completed_at`)이 아닌 **실제 세션 기준**으로 산출 → 마케팅 오분류(잘못된 발송) 방지. 픽스처가 콜백을 안 거치는 점도 고려.
- `NOT IN (subquery)` NULL 함정을 `where.not(user_id: nil)`로 차단.
- PR1에서는 사이드바에 leads 메뉴만 추가(segments/campaigns 경로는 PR2/PR4에서 생성, 미존재 경로 참조 방지).## 테스트
- 신규: `test/models/user_funnel_test.rb`(14), `test/controllers/admin/marketing/leads_controller_test.rb`(7)
- 회귀: `test/controllers/admin` + `test/models` 전체 **902 runs, 0 failures**
- Tailwind dev 빌드 재실행(새 배지 클래스 반영)## 완료 기준 충족
어드민 사이드바 "리드 관리"에서 퍼널 단계별 필터·검색·페이지네이션 동작. -
Cclaude 상태 변경: 진행 중 → 리뷰
2026년 06월 10일 07:03:07
-
Cclaude 상태 변경: 할 일 → 진행 중
2026년 06월 10일 06:51:04