Commit Graph

57 Commits (273221727c7c53a3931a91aed232f48baa7f2b79)

Author SHA1 Message Date
Haewon Kam 273221727c fix: Website SNS 링크 5개 발견 + 네이버 카페 5,984명 분석 추가
- websiteAudit: snsLinksOnSite false→true, Footer SNS 5개 링크 상세 추가
  (Blog, Facebook, Instagram, YouTube, Naver Cafe)
- Website 점수 52→65 상향, tracking 6개로 보강
- 네이버 카페 "뷰성형외과 성형의 모든것" 회원 5,984명 otherChannels 추가
- mockPlan: Naver Cafe 채널 전략 추가 (20개 게시판 구조 기반)
- WebsiteAudit 타입에 snsLinksDetail 옵셔널 필드 추가
- problemDiagnosis SNS 단절 진단 수정 (Footer 있으나 Header에는 없음)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 13:23:18 +09:00
Haewon Kam 15099e8e1a feat: 뷰성형외과 실제 데이터 기반 세일즈 프로토타입 완성
- 9개 채널 실제 데이터 수집 (Firecrawl + Chrome MCP)
- mockReport.ts: 강남언니 9.5점/19,030리뷰 (TODO 해결), 채널 점수 갱신,
  Instagram 70K/14K 실제 수치, Facebook TH 페이지 발견, Naver Blog 활성화
- mockPlan.ts: Naver Blog 미확인→활성 550개, YouTube 104K 반영
- useReport: id='view-clinic' demo fallback 추가
- useMarketingPlan: id='view-clinic' demo fallback 추가
- Viewclinic Plan.md: 수집 데이터 전체 기록

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 13:11:18 +09:00
Haewon Kam 2a1a6f20fb fix: URL 파싱 에러 방어 처리 (transformReport, ClinicProfilePage)
metadata.url 또는 row.url이 null/빈 문자열일 때
new URL() 호출이 throw하던 문제를 try/catch로 방어.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 15:31:56 +09:00
Haewon Kam aabba1534b fix: 로딩 화면 분석 프로세스 텍스트 영어 통일
- planning 단계 'AI 콘텐츠 전략 생성 중...' → 'Generating AI content strategy...'
- labelDone '콘텐츠 플랜 생성 완료' → 'Content plan generated'

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 17:24:13 +09:00
Haewon Kam c0c37b84de fix: naverBlog RSS 전환 + naverPlace DB-first 패턴 + 핵심문제진단 JSON 렌더링 버그 수정
- collect-channel-data: naverBlog 실시간 검색 제거 → verified handle 기반 RSS 직접 fetch
- collect-channel-data: naverPlace DB-first 패턴 (verified_channels에 저장된 데이터 우선 사용, 없을 때만 URL도메인 매칭 검색 후 DB에 저장)
- transformReport: ch.issues 배열 항목이 {issue, severity} 객체일 때 JSON.stringify 대신 .issue 문자열 추출
- ProblemDiagnosis: Lucide 아이콘 제거 → FilledIcons(ShieldFilled, FileTextFilled, LinkExternalFilled), 항목 구분자 ' ' → ' — '

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 17:17:22 +09:00
Haewon Kam 2027ae9b64 feat: 마케팅 플랜 Phase 1~3 완성
- ContentCalendar: 드래그앤드롭(주차 내 요일 간 이동) + 엔트리 추가 버튼 + iCal Export
- BrandingGuide: 색상 팔레트 인라인 편집(스와치 클릭 → hex 팝오버) + DO/DON'T 2컬럼
- WorkflowTracker: 콘텐츠 제작 파이프라인(기획→AI초안→검토→승인→배포), 동영상/이미지+텍스트 분류
- RepurposingProposal: YouTube 인기 영상 리퍼포징 제안 아코디언 섹션
- AssetDetailModal: 에셋 카드 클릭 시 상세 모달
- 디자인 시스템 감사: Lucide 라인 아이콘 제거, 원색(pink/indigo/purple) 제거, 이모지 UI 제거
- "My Assets" → "나의 소재" 일관성 변경
- FilledIcons: DownloadFilled, RocketFilled 추가

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 16:44:21 +09:00
Haewon Kam 9c4d10609f feat: 스크린샷 리포트 반영 + 영구 저장 인프라 강화
- transformReport: channel_data.screenshots → report.screenshots 자동 매핑
- transformReport: youtubeAudit/instagramAudit/facebookAudit diagnosis에 evidenceIds 자동 연결 (채널별 스크린샷 → 진단 항목 연결)
- collect-channel-data: 스크린샷 아카이브를 병렬→순차로 변경 (rate-limit 방지), 실패 시 상세 로그
- scripts/archive-screenshots.py: 기존 GCS 임시 URL → Supabase Storage 일괄 재아카이브 스크립트 추가
- TypeScript 기존 에러 3개 수정 (SectionErrorBoundary, ClinicSnapshot, reviewCount 유니언 타입)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 14:43:25 +09:00
Haewon Kam 2d1937944a fix: 리포트 데이터 정확도 개선 + 강남언니·인스타그램 스크래핑 데이터 반영
- ClinicSnapshot: 내부 관리용 배지(Registry 검증·분점·등급) 병원 리포트에서 제거
- transformReport: Facebook 리뷰수 파싱 ("Not yet rated (3 Reviews)" 정규식 추출)
- transformReport: 네이버 플레이스 KPI 목표가 현재값보다 낮은 오류 수정 (동적 계산)
- transformReport: 네이버 블로그 방문자 "0(미운영)" → "검색 노출 N건 (방문자 비공개)"
- transformReport: 웹사이트+SNS 유입 "0%" → "측정 불가 (트래킹 미설치)"
- clinic_registry_working.csv: gangnam_unni_badges, gangnam_unni_procedures 컬럼 추가 (60개 병원)
- clinic_registry_working.csv: instagram_followers, instagram_posts 컬럼 추가 (64개 병원)
- INFINITH_Outbound_List.csv: 인스타그램 팔로워·게시물수 컬럼 추가 (64개 병원)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 14:18:31 +09:00
Haewon Kam 6e8f6940bf fix: gangnamUnni always-try + leadDoctor in Perplexity prompt
- collect-channel-data: gangnamUnni scraping no longer requires
  verified=true. Fallback: Firecrawl search for gangnamunni.com URL
  when discover-channels failed to verify. Solves empty ratings/reviews.
- generate-report: Perplexity prompt now explicitly requests leadDoctor
  (name, specialty, rating, reviewCount) and staffCount in clinicInfo.
- transformReport: clinicInfo type extended with leadDoctor + staffCount;
  transformation prefers clinic.leadDoctor over doctors[0] fallback.

Root cause: clinic_registry table not yet in DB → discover-channels
always falls back to API search → gangnamUnni URL not found →
collect-channel-data skips gangnamUnni → all clinic metrics empty.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 10:29:10 +09:00
Haewon Kam 2cda26a649 feat: per-URL clinic folder — auto-save all scraped data to Storage
Each analysis run now creates a dedicated folder in Supabase Storage:
  clinics/{domain}/{reportId}/
    ├── scrape_data.json    (discover-channels: website scrape + Perplexity)
    ├── channel_data.json   (collect-channel-data: all channel API results)
    └── report.json         (generate-report: final AI-generated report)

Screenshots also moved from {reportId}/{id}.png to:
  clinics/{domain}/{reportId}/screenshots/{id}.png

Migration: 20260407_clinic_data_storage.sql creates 'clinic-data' bucket
(private, 10MB/file, JSON only). All writes are non-fatal — pipeline
continues even if Storage upload fails.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 10:04:52 +09:00
Haewon Kam ae87953fa0 feat: Registry-verified badge + registryData data flow + V3 error recording
- ClinicSnapshot.tsx: 'Registry 검증' badge (ShieldCheck icon), district/branches/brandGroup pills, external links (강남언니/네이버플레이스/구글맵) when source=registry
- report.ts: add source and registryData fields to ClinicSnapshot type
- transformReport.ts: ApiMetadata now accepts source/registryData; passes to clinicSnapshot
- useReport.ts: DB load path extracts scrape_data.source + scrape_data.registryData → transformApiReport
- V3 dual-write error recording: discover-channels, collect-channel-data, generate-report now write error_message + error status to analysis_runs on catch instead of silently swallowing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 10:01:19 +09:00
Haewon Kam d5f7f24e0a feat: clinic registry DB + pipeline audit P0 fixes
## Clinic Registry
- data/clinic-registry/clinic_registry_working.csv — 91개 병원 채널 마스터 DB
- data/clinic-registry/INFINITH_Outbound_List.csv — BD팀 아웃바운드 리스트 (17컬럼)
- data/clinic-registry/update_csv.py — 안전 CSV 업데이트 스크립트 (빈 필드만 채움)
- data/clinic-registry/extract_place_ids.py — 네이버 플레이스 ID 추출기
- scripts/import-registry.ts — CSV → Supabase clinic_registry 테이블 임포트
- supabase/migrations/20260406_clinic_registry.sql — clinic_registry 테이블 스키마

## Pipeline P0 Bug Fixes (전수 감사 후)
- fix(collect-channel-data): 강남언니 rating 0-10 스케일 오변환 제거
  - 기존: rating ≤ 5이면 ×2 → 4.8/10을 9.6/10으로 잘못 변환
  - 수정: Firecrawl 프롬프트가 이미 0-10 지시 → rawValue 직접 신뢰
- fix(generate-report): Perplexity 단일 fetch → fetchWithRetry 교체
  - maxRetries:2, backoffMs:[5000,15000], timeoutMs:90s
  - 기존: 타임아웃/429 시 리포트 생성 전체 실패
  - 수정: 자동 재시도로 일시적 API 오류 극복

## Docs
- docs/PIPELINE_IMPROVEMENT_PLAN.md — Sprint 0/1/2 완료 표시 + 전수 감사 결과 추가
- docs/REGISTRY_FUNCTIONAL_SPECS.md, DB_SCHEMA_V3.md 외 기획 문서 다수 추가

## New Components & Features
- supabase/functions/generate-content-plan, adjust-strategy — 콘텐츠 플랜/전략 조정
- src/components/plan/EditEntryModal, StrategyAdjustmentSection — 플랜 편집 UI
- supabase/functions/_shared/dataQuality, foundingYearExtractor, urlClassifier — 데이터 품질 유틸

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 09:33:25 +09:00
Haewon Kam ec991057e6 feat: add API Dashboard + filled icons + pipeline improvements
- Add /api-dashboard page with API connection status, env var checker,
  pipeline flow diagram, and cost estimator
- Add 15 new filled SVG icons (Shield, Database, Server, Bolt, Eye,
  Copy, Check, Cross, Warning, Refresh, Flow, Coin, LinkExternal etc.)
- Follow INFINITH design system: no emoji, no line icons, semantic
  status colors, diagonal shadows, brand gradients, font-serif headings
- Improve Vision Analysis with base64 encoding fix
- Add SectionErrorBoundary for graceful section-level error handling
- Add Google Places API utility (prepared for future migration)
- Fix Edge Function auth headers and report generation pipeline

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 14:59:31 +09:00
Haewon Kam d1157da39c fix: restore button active state + inject gangnamunni Vision data into report
- Hero button: gray when empty, accent gradient when URL entered
- generate-report: force-inject gangnamUnniStats from Vision Analysis
  into channelAnalysis (score, rating, reviews, doctors)
- Add gangnamunni Vision data to prompt context

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 11:53:33 +09:00
Haewon Kam 79950925a1 fix: add Authorization header to all Edge Function calls + fix Vision Analysis
- All fetch calls to Supabase Edge Functions now include
  Authorization: Bearer <anon_key> (was missing → 401 errors)
- Fix Firecrawl screenshot API: remove invalid screenshotOptions,
  use "screenshot@fullPage" format (v2 API compatibility)
- Fix screenshot response handling: v2 returns URL not base64,
  now downloads and converts to base64 for Gemini Vision
- Add about page to Vision Analysis capture targets
- Add retry utility, channel error tracking, pipeline resume,
  enrichment retry, EmptyState improvements (Sprint 2-3)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-05 10:08:03 +09:00
Haewon Kam 7fe3ff82c9 feat: DB V3 dual-write — clinics + analysis_runs + channel_snapshots
Phase 2-4 of SaaS schema migration. All Edge Functions now write to
BOTH legacy marketing_reports AND new V3 tables:

discover-channels:
  - UPSERT clinics (url-based dedup)
  - INSERT analysis_runs (status: discovering)

collect-channel-data:
  - INSERT channel_snapshots (one per channel — time-series!)
  - INSERT screenshots (evidence rows)
  - UPDATE analysis_runs (raw_channel_data, vision_analysis)

generate-report:
  - UPDATE analysis_runs (report, status: complete)
  - UPDATE clinics (last_analyzed_at, established_year)

Frontend passes clinicId + runId through all 3 phases.
Legacy marketing_reports still written for backward compatibility.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 00:51:11 +09:00
Haewon Kam 80c57147e7 feat: Sprint 1 — 7 data quality quick wins
WP-1: YouTube channel ID regex {20,} → {22} (exactly 24 chars)
WP-2: Naver Place category filtering in enrich-channels (성형/피부)
WP-3: Google Maps stores mapsUrl separately from clinicWebsite
WP-4: Naver Blog separates officialBlogUrl from search results
WP-5: 강남언니 rawRating + normalized rating (≤5 → ×2), Firecrawl
      prompt explicitly states "out of 10, NOT out of 5"
WP-6: Perplexity model centralized in _shared/config.ts (env override)
WP-7: Apify Instagram timeout 30s → 45s

Frontend: transformReport uses mapsUrl and officialBlogUrl when available

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 23:35:40 +09:00
Haewon Kam 29c1faf49e fix: correct OtherChannels URLs — Google Maps, Naver Blog, Naver Place
Google Maps: was using gm.website (clinic's own site) → now always
generates maps.google.com/search URL

Naver Blog: was linking to first search result post (random personal
blog) → now links to Naver blog search results page

Naver Place: np.link was the clinic's own website, not Naver Place →
now generates map.naver.com search URL. Also fixed collect-channel-data
to search with "성형외과" suffix and match by category (성형/피부) to
avoid same-name dental clinics.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 21:25:26 +09:00
Haewon Kam a02c83155e fix: remove duplicate Facebook link at bottom — now only on page name
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 16:28:46 +09:00
Haewon Kam 4928d24ace feat: consistent ExternalLink on all channel names — Facebook + OtherChannels
Facebook pageName now links to facebook.com/{url} with ExternalLink icon.
OtherChannels: moved ExternalLink from right-end to inline with channel
name, matching the Instagram/YouTube pattern.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 16:23:29 +09:00
Haewon Kam 2a35108149 fix: '의료진 3명' → '전문의 3명' — staffCount는 강남언니 등록 의사 수
staffCount is the number of registered doctors from 강남언니, not total
staff. Label changed from 의료진(medical staff) to 전문의(specialists)
to accurately reflect the data source. Diagnosis message also updated.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 16:21:10 +09:00
Haewon Kam 66b4826f55 feat: add clickable source links to all report sections
YouTubeAudit: handle links to youtube.com/@{handle} with ExternalLink icon
InstagramAudit: handle links to instagram.com/{handle} with ExternalLink icon
ClinicSnapshot: domain is now clickable link, phone is tel: link
OtherChannels: Google Maps generates search URL, Naver Blog links to
  first blog post or search results (previously empty string)
transformReport: fills missing URL fields for Google Maps and Naver Blog

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 11:02:35 +09:00
Haewon Kam 163751410f fix: reduce blob animation range + force GPU with translate3d
Halved blob movement (30px→15px, 8vw→4vw) and reduced scale
(1.1→1.05). Using translate3d forces GPU compositing layer,
preventing main-thread layout recalculation that causes jitter.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 00:48:42 +09:00
Haewon Kam c07f839773 fix: GPU-accelerate blob animations to prevent layout jitter
Added will-change:transform, contain:layout style, and
backface-visibility:hidden to all blob animations. This promotes
blobs to their own GPU compositing layer, preventing them from
triggering main-thread reflow/repaint that causes page shaking.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 00:46:26 +09:00
Haewon Kam eb058ab2e5 fix: add overflow-x:hidden to html to stop blob-caused layout shaking
Animated blobs with translate+scale overflow their containers,
causing horizontal scrollbar to flicker and page to jitter.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 00:40:47 +09:00
Haewon Kam 9a141f3603 fix: thin overlay scrollbar to equalize left/right margins
Uses scrollbar-width:thin + 6px webkit scrollbar to minimize
layout shift from the default ~15px scrollbar. Keeps original
design intact while reducing visual asymmetry.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 00:15:00 +09:00
Haewon Kam c6b39e3706 revert: restore Hero, Modules, CSS to exact original version (922ec8f)
Reverts to the original design with full-color button (no disabled
opacity), original background gradients, and original blob decorations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 00:07:18 +09:00
Haewon Kam 46b911d4c0 fix: remove opacity-50 from Analyze button — always show full color
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 00:05:50 +09:00
Haewon Kam 0d72750982 revert: restore Hero, Modules, CSS to original state
All layout changes (blob modifications, overflow-x/clip, scrollbar-gutter)
reverted to the original version that was working correctly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 23:56:58 +09:00
Haewon Kam 81d673a3e8 fix: use overflow-y:overlay so scrollbar doesn't shift layout
Removes scrollbar-gutter:stable which pushed content left.
overlay scrollbar floats over content with zero layout impact.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 23:54:32 +09:00
Haewon Kam ef5d9fed0f fix: scrollbar-gutter:stable to balance left/right margins
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 23:53:01 +09:00
Haewon Kam 0f517473e0 fix: remove asymmetric blobs from Hero, use centered gradient
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 23:48:15 +09:00
Haewon Kam 7154e76bc4 fix: balance Hero blob sizes and opacity for symmetric layout
Reduced blob size (w-64→w-48), lowered opacity (30%→20%), used
percentage positioning (left-10→left-[10%]), and wrapped in
overflow-clip container. Eliminates visual weight asymmetry.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 23:45:28 +09:00
Haewon Kam 2b7494305a fix: contain all overflowing blobs within viewport
- Modules.tsx: wrap blobs in overflow-clip container, use max-w instead
  of min-w to prevent viewport overflow
- index.css: add #root { overflow-x:clip; max-width:100vw } as final
  safety net — no child element can expand beyond viewport

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 23:42:35 +09:00
Haewon Kam 71c56783ed fix: use overflow-x:clip instead of hidden to fully contain blur blobs
overflow-x:hidden doesn't clip CSS blur() filter radius, allowing
blurred blobs to still cause horizontal scrollbar. overflow-x:clip
fully contains all rendered pixels including blur.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 23:38:38 +09:00
Haewon Kam a6bb31a093 fix: restore English loading steps, hide channel panel, fix blank report page
- Loading steps back to English (Scanning website, Collecting data, etc.)
- Removed verified channels panel from loading screen
- Fixed blank report page: detect empty report JSON from DB and show
  appropriate error message instead of rendering empty components
- Navigation state: only pass if report+metadata exist

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 23:35:06 +09:00
Haewon Kam caac2f22c5 fix: prevent horizontal overflow causing right-shifted layout
Animated blur blobs and absolute-positioned elements were overflowing
the viewport, creating a horizontal scrollbar that shifted all content.
Added overflow-x: hidden to html and body.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 23:30:27 +09:00
Haewon Kam 9d06272073 fix: KPI uses real enrichment data instead of AI guesses
buildKpiDashboard now reads channelEnrichment (real API data from
Phase 2) with fallback to channelAnalysis (AI-generated). YouTube
subscribers, Instagram followers, 강남언니 rating/reviews all use
verified data when available. Fixed || ?? operator precedence.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 22:28:11 +09:00
Haewon Kam ff82c9f9d5 fix: KPI dashboard always generates comprehensive 10+ metrics
Previously AI-provided kpiTargets (often only 3-4 items) would
completely replace our channel-based KPI generation. Now we always
build the full set (YouTube, Instagram, Naver, 강남언니, Google Maps,
cross-platform) and merge AI extras that don't overlap.

Also adds 강남언니 평점/리뷰, 네이버 플레이스 평점 as standard KPIs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 22:22:40 +09:00
Haewon Kam 7557ef774c feat: Pipeline V2 — 3-phase analysis with verified channel discovery
Restructured the entire analysis pipeline from AI-guessing social
handles to deterministic 3-phase discovery + collection + generation.

Phase 1 (discover-channels): 3-source channel discovery
  - Firecrawl scrape: extract social links from HTML
  - Perplexity search: find handles via web search
  - URL regex parsing: deterministic link extraction
  - Handle verification: HEAD requests + YouTube API
  - DB: creates row with verified_channels + scrape_data

Phase 2 (collect-channel-data): 9 parallel data collectors
  - Instagram (Apify), YouTube (Data API v3), Facebook (Apify)
  - 강남언니 (Firecrawl), Naver Blog + Place (Naver API)
  - Google Maps (Apify), Market analysis (Perplexity 4x parallel)
  - DB: stores ALL raw data in channel_data column

Phase 3 (generate-report): AI report from real data
  - Reads channel_data + analysis_data from DB
  - Builds channel summary with real metrics
  - AI generates report using only verified data
  - V1 backwards compatibility preserved (url-based flow)

Supporting changes:
  - DB migration: status, verified_channels, channel_data columns
  - _shared/extractSocialLinks.ts: regex-based social link parser
  - _shared/verifyHandles.ts: multi-platform handle verifier
  - AnalysisLoadingPage: real 3-phase progress + channel panel
  - useReport: channel_data column support + V2 enrichment merge
  - 강남언니 rating: auto-correct 5→10 scale + search fallback
  - KPIDashboard: navigate() instead of <a href>
  - Loading text: 20-30초 → 1-2분

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 21:49:13 +09:00
Haewon Kam da267fd744 feat: Content Director engine + report diagnosis/roadmap/KPI overhaul + PDF export fix
- Content Director (contentDirector.ts): deterministic 4-week editorial
  calendar engine — pillar-service matrix, channel-format slots, weekly
  themes (브랜드 정비 → 콘텐츠 엔진 → 소셜 증거 → 전환 최적화)
- transformPlan.ts: buildCalendar() delegates to Content Director with
  enrichment data (YouTube videos for repurposing)
- transformReport.ts: buildTransformation() generates rich per-channel
  platform strategies; buildRoadmap() creates Foundation/Content Engine/
  Optimization 3-phase plan; buildKpiDashboard() generates 10+ channel-
  specific metrics with targets
- ProblemDiagnosis: clustered 3 core issues (brand/content/funnel) in
  glass cards + expandable detail list
- RoadmapTimeline: Foundation/Content Engine/Optimization structure
- KPIDashboard: formatKpiValue() for human-readable numbers (150K, 1.5M)
- YouTubeAudit: metric-based diagnosis rows (subscriber ratio, upload freq)
- ContentCalendar: week theme labels, channel symbols, compact entries
- useExportPDF: triggerAllAnimations() scrolls all sections to fire
  whileInView before capture; isDarkSection() keeps dark sections whole;
  forceVisible() 2-pass opacity/transform override

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 20:07:39 +09:00
Haewon Kam e32b8766de feat: prototype gap closure — enrichment diagnosis + brand extraction + plan assets
Phase 1: Data Pipeline Fixes
- Plan page: connect enrichment data for Asset Collection + YouTube Repurpose
- mergeEnrichment: generate 15-20 data-driven diagnosis items from enrichment
  (YouTube Shorts check, IG engagement, FB activity, 강남언니 ratings, GMaps)
- ClinicSnapshot: fill staffCount, nearestStation, certifications from enrichment

Phase 2: AI + Brand Enhancement
- AI prompt: per-channel diagnosis[] array (5-7 items), established, nameEn, newChannelProposals
- scrape-website: Firecrawl branding extraction (colors, fonts, logo, tagline)
- transformPlan: BrandGuide colors/fonts from scraped branding data
- transformPlan: cross-channel brand consistency analysis (name, phone mismatches)
- transformPlan: channel branding rules from enrichment (YT, IG, FB profiles)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 17:09:15 +09:00
Haewon Kam a7d8aeeddc feat: Facebook page data collection via Apify scraper
- enrich-channels: add Facebook Pages Scraper (apify~facebook-pages-scraper)
- Collects: pageName, followers, likes, categories, email, phone, website, intro, rating
- transformReport: merge Facebook data into facebookAudit.pages[] (auto-shows section)
- Frontend: pass facebookHandle through enrichment pipeline
- EnrichChannelsRequest: add facebookHandle parameter

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 16:16:37 +09:00
Haewon Kam ad625e08ee fix: enrichment pipeline reliability + loading page gradient + button click area
- generate-report: filter empty strings from AI social handles, add saveError logging
- useReport: 3-level fallback for social handles (report > clinicInfo > scrape_data)
- useEnrichment: always trigger enrichment if clinicName exists (not just IG/YT handles)
- Hero: pointer-events-none on decorative blobs (were blocking button clicks)
- AnalysisLoadingPage: warm gradient on INFINITH logo text (#fff3eb → #e4cfff → #f5f9ff)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 16:05:33 +09:00
Haewon Kam 72ea8f4a2d feat: Naver Search API + multi-account Instagram + button UX fix
- Naver Blog search: collect blog post results for clinic name (total count + top 10 posts)
- Naver Place search: collect place info (name, category, address, telephone)
- Multi-account Instagram: AI prompt requests all IG accounts (국내/해외)
- enrich-channels: process multiple IG handles with fallback per handle
- transformReport: merge multiple IG accounts into instagramAudit.accounts[]
- generate-report: socialHandles.instagram now array of handles
- Hero/CTA: transition-all → transition-shadow for instant click response
- Hero/CTA: disabled state when URL is empty (opacity-50 + cursor-not-allowed)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 15:34:10 +09:00
Haewon Kam cf482d1bd7 feat: 강남언니 real-time data collection via Firecrawl scraping
- enrich-channels: add 강남언니 scraping module (search + structured JSON extraction)
- Collects: rating/10, reviews, doctors with ratings, procedures, certifications
- transformReport: merge 강남언니 data into clinicSnapshot + otherChannels
- Updates lead doctor info, certifications, and review counts from real data

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 14:51:47 +09:00
Haewon Kam 9bf47f7d93 feat: Creatomate API integration — real video generation in Content Studio
- Add creatomateVideoGen.ts service with polling-based async rendering
- Replace video stub (setTimeout) with actual Creatomate API calls
- Add video preview (<video> tag) and MP4 download support
- Build programmatic source (branded slideshow) without pre-built templates
- Error handling: auth, rate limit, render failure → Korean messages

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 14:13:07 +09:00
Haewon Kam 200497fa1e feat: P1-5/6/7 — AI KPI targets, website tech audit, dynamic clinic profile
- P1-5: Add kpiTargets schema to AI prompt, use AI-generated goals instead of hardcoded multipliers
- P1-6: Extend website channelAnalysis with trackingPixels, snsLinksOnSite, additionalDomains, mainCTA
- P1-7: ClinicProfilePage fetches data from DB by report ID instead of hardcoded VIEW clinic data

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 14:30:03 +09:00
Haewon Kam 7ea9972c7e feat: P1-4 Brand Identity tab — AI-generated brand analysis
- generate-report: add brandIdentity schema to AI prompt (logo, message, tone, positioning, hashtags, channel consistency)
- transformReport: map API brandIdentity array to TransformationProposal component
- ApiReport type: add brandIdentity field

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 14:10:28 +09:00
Haewon Kam 4484ac788a feat: P0 fixes — date formatting, channel labels, dynamic marketing plan
- ReportHeader/PlanHeader: format ISO dates as Korean (2026년 4월 2일)
- ChannelOverview: map API keys to Korean labels (naverBlog → 네이버 블로그)
- useMarketingPlan: replace mockPlan with real DB-based plan generation
- transformPlan: build MarketingPlan from report data (channels, pillars, calendar)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 13:58:40 +09:00