diff --git a/src/lib/transformReport.ts b/src/lib/transformReport.ts index 7c1ae9c..616d78f 100644 --- a/src/lib/transformReport.ts +++ b/src/lib/transformReport.ts @@ -332,19 +332,8 @@ function buildRoadmap(r: ApiReport): import('../types/report').RoadmapMonth[] { } function buildKpiDashboard(r: ApiReport): import('../types/report').KPIMetric[] { - // If AI provided explicit KPIs, use them - if (r.kpiTargets?.length) { - return r.kpiTargets - .filter((k): k is { metric?: string; current?: string; target3Month?: string; target12Month?: string } => !!k?.metric) - .map(k => ({ - metric: k.metric || '', - current: k.current || '-', - target3Month: k.target3Month || '-', - target12Month: k.target12Month || '-', - })); - } - - // Build comprehensive KPI from channel data + // Always build comprehensive KPIs from channel data. + // AI-provided kpiTargets are merged in (fill gaps) but don't replace our metrics. const channels = r.channelAnalysis || {}; const metrics: import('../types/report').KPIMetric[] = []; @@ -404,6 +393,36 @@ function buildKpiDashboard(r: ApiReport): import('../types/report').KPIMetric[] }); } + // 강남언니 + if (channels.gangnamUnni) { + const rating = channels.gangnamUnni.rating ?? 0; + const correctedRating = typeof rating === 'number' && rating > 0 && rating <= 5 ? rating * 2 : rating; + metrics.push({ + metric: '강남언니 평점', + current: correctedRating > 0 ? `${correctedRating}/10` : '-', + target3Month: correctedRating > 0 ? `${Math.min(correctedRating + 0.5, 10).toFixed(1)}/10` : '8.0/10', + target12Month: correctedRating > 0 ? `${Math.min(correctedRating + 1.0, 10).toFixed(1)}/10` : '9.0/10', + }); + if (channels.gangnamUnni.reviews) { + metrics.push({ + metric: '강남언니 리뷰 수', + current: fmt(channels.gangnamUnni.reviews as number), + target3Month: fmt(Math.round((channels.gangnamUnni.reviews as number) * 1.15)), + target12Month: fmt(Math.round((channels.gangnamUnni.reviews as number) * 1.5)), + }); + } + } + + // Google Maps + if (channels.naverPlace) { + metrics.push({ + metric: '네이버 플레이스 평점', + current: channels.naverPlace.rating ? `${channels.naverPlace.rating}/5` : '-', + target3Month: '4.5/5', + target12Month: '4.8/5', + }); + } + // Cross-platform metrics.push({ metric: '웹사이트 + SNS 유입', @@ -419,6 +438,33 @@ function buildKpiDashboard(r: ApiReport): import('../types/report').KPIMetric[] target12Month: '월 50건', }); + // Merge AI-provided KPIs that we didn't already cover + if (r.kpiTargets?.length) { + const existingNames = new Set(metrics.map(m => m.metric.toLowerCase())); + for (const k of r.kpiTargets) { + if (!k?.metric) continue; + // Skip if we already have a similar metric + const lower = k.metric.toLowerCase(); + if (existingNames.has(lower)) continue; + if (lower.includes('youtube') && existingNames.has('youtube 구독자')) continue; + if (lower.includes('instagram') && existingNames.has('instagram kr 팔로워')) continue; + if (lower.includes('강남언니') || lower.includes('gangnam')) { + // Use AI's gangnamunni data — update existing or add + const guIdx = metrics.findIndex(m => m.metric.includes('강남언니')); + if (guIdx >= 0) { + metrics[guIdx] = { metric: k.metric, current: k.current || '-', target3Month: k.target3Month || '-', target12Month: k.target12Month || '-' }; + continue; + } + } + metrics.push({ + metric: k.metric, + current: k.current || '-', + target3Month: k.target3Month || '-', + target12Month: k.target12Month || '-', + }); + } + } + return metrics; }