import { useState, useEffect } from 'react'; import { motion } from 'motion/react'; import { SectionWrapper } from '../report/ui/SectionWrapper'; import { MegaphoneFilled } from '../icons/FilledIcons'; import { triggerStrategyAdjustment } from '../../lib/supabase'; import { db } from '../../lib/db'; interface StrategyAdjustmentSectionProps { clinicId: string | null; planId?: string; } interface KpiProgress { metric: string; current: string; target: string; progress: number; } interface StrategySuggestion { adjustmentType: string; channel: string; description: string; reason: string; priority: 'high' | 'medium' | 'low'; beforeValue: string; afterValue: string; } interface AdjustmentHistoryItem { id: string; adjustment_type: string; description: string; reason: string; created_at: string; } const priorityColors: Record = { high: { bg: 'bg-red-50', text: 'text-red-700', dot: 'bg-red-400' }, medium: { bg: 'bg-amber-50', text: 'text-amber-700', dot: 'bg-amber-400' }, low: { bg: 'bg-blue-50', text: 'text-blue-700', dot: 'bg-blue-400' }, }; const typeLabels: Record = { frequency_change: '게시 빈도 조정', pillar_shift: '콘텐츠 필러 전환', channel_add: '채널 추가', channel_pause: '채널 일시 중지', content_format_change: '포맷 변경', tone_shift: '톤 조정', other: '기타 조정', }; export default function StrategyAdjustmentSection({ clinicId, planId }: StrategyAdjustmentSectionProps) { const [kpiProgress, setKpiProgress] = useState([]); const [suggestions, setSuggestions] = useState([]); const [history, setHistory] = useState([]); const [overallAssessment, setOverallAssessment] = useState(''); const [isLoading, setIsLoading] = useState(false); const [isRunning, setIsRunning] = useState(false); const [hasData, setHasData] = useState(false); useEffect(() => { if (!clinicId) return; loadExistingData(); }, [clinicId]); async function loadExistingData() { setIsLoading(true); try { // Load latest performance metrics const { data: perf } = await db<{ kpi_progress: unknown; strategy_suggestions: unknown }>('perf.latest', { clinicId: clinicId! }); if (perf) { setKpiProgress((perf.kpi_progress as KpiProgress[]) || []); setSuggestions((perf.strategy_suggestions as StrategySuggestion[]) || []); setHasData(true); } // Load adjustment history const { data: adj } = await db('adjustments.list', { clinicId: clinicId! }); if (adj) setHistory(adj); } catch { // No data yet — show empty state } finally { setIsLoading(false); } } async function handleRunAdjustment() { if (!clinicId || isRunning) return; setIsRunning(true); try { const result = await triggerStrategyAdjustment(clinicId); if (result.success) { setKpiProgress(result.kpiProgress || []); setSuggestions(result.adjustments?.strategySuggestions || []); setOverallAssessment(result.adjustments?.overallAssessment || ''); setHasData(true); await loadExistingData(); // Refresh history } } catch (err) { console.error('Strategy adjustment failed:', err); } finally { setIsRunning(false); } } if (isLoading) { return (
); } return ( {/* Action Button */}

채널 성과 데이터를 분석하여 콘텐츠 전략을 자동 조정합니다

{!hasData ? ( /* Empty state */

아직 성과 데이터가 없습니다

채널 분석을 2회 이상 실행하면 성과 비교 및 전략 조정이 가능합니다

) : ( <> {/* Overall Assessment */} {overallAssessment && (

{overallAssessment}

)} {/* KPI Progress */} {kpiProgress.length > 0 && (

KPI 달성률

{kpiProgress.map((kpi, i) => (
{kpi.metric} = 100 ? 'text-green-600' : kpi.progress >= 70 ? 'text-amber-600' : 'text-red-600' }`}> {kpi.progress}%
= 100 ? 'bg-green-400' : kpi.progress >= 70 ? 'bg-amber-400' : 'bg-red-400' }`} initial={{ width: 0 }} animate={{ width: `${Math.min(kpi.progress, 100)}%` }} transition={{ duration: 0.8, delay: i * 0.1 }} />
현재: {kpi.current} 목표: {kpi.target}
))}
)} {/* Strategy Suggestions */} {suggestions.length > 0 && (

전략 조정 제안

{suggestions.map((s, i) => { const colors = priorityColors[s.priority] || priorityColors.medium; return (
{typeLabels[s.adjustmentType] || s.adjustmentType} {s.channel && ( {s.channel} )}

{s.description}

{s.reason}

{s.beforeValue && s.afterValue && (
{s.beforeValue} → {s.afterValue}
)}
); })}
)} {/* Adjustment History */} {history.length > 0 && (

조정 이력

{history.map((item) => (
{typeLabels[item.adjustment_type] || item.adjustment_type} {new Date(item.created_at).toLocaleDateString('ko-KR')}

{item.description}

))}
)} )} ); }