[2ff88f42] feat(GTM-Engine): Implement Dual Opener Strategy & Harden Analysis

Completed the GTM engine setup:\n\n- Implemented 'Dual Opener' generation (Primary/Secondary) in ClassificationService.\n- Migrated DB to support two opener fields.\n- Updated API and Frontend to handle and display both openers.\n- Fixed bug creating duplicate website_scrape entries.\n- Hardened metric extraction by improving the LLM prompt and adding content length checks.
This commit is contained in:
2026-02-20 15:38:06 +00:00
parent 40fec23ccb
commit 1aabae30ad
12 changed files with 434 additions and 19 deletions

View File

@@ -57,6 +57,10 @@ type CompanyDetail = {
// Industry Strategy (V2)
industry_details?: IndustryDetails
// Marketing AI (V3)
ai_opener: string | null
ai_opener_secondary: string | null
// NEU v0.7.0: Quantitative Metrics
calculated_metric_name: string | null
calculated_metric_value: number | null
@@ -453,6 +457,43 @@ export function Inspector({ companyId, initialContactId, onClose, apiBase }: Ins
)
}
// Marketing AI Card Renderer
const renderMarketingCard = () => {
if (!data?.ai_opener && !data?.ai_opener_secondary) return null;
return (
<div className="bg-orange-50 dark:bg-orange-900/10 rounded-xl p-5 border border-orange-100 dark:border-orange-900/50 mb-6">
<h3 className="text-sm font-semibold text-orange-700 dark:text-orange-300 uppercase tracking-wider mb-3 flex items-center gap-2">
<Bot className="h-4 w-4" /> Marketing AI (Openers)
</h3>
<div className="space-y-4">
{data.ai_opener && (
<div className="p-3 bg-white dark:bg-slate-900 rounded border border-orange-200 dark:border-orange-800">
<div className="flex justify-between items-center mb-1">
<div className="text-[10px] text-orange-600 dark:text-orange-400 uppercase font-bold tracking-tight">Primary: Infrastructure/Cleaning</div>
</div>
<div className="text-sm text-slate-700 dark:text-slate-200 leading-relaxed italic">"{data.ai_opener}"</div>
</div>
)}
{data.ai_opener_secondary && (
<div className="p-3 bg-white dark:bg-slate-900 rounded border border-orange-200 dark:border-orange-800">
<div className="flex justify-between items-center mb-1">
<div className="text-[10px] text-orange-600 dark:text-orange-400 uppercase font-bold tracking-tight">Secondary: Service/Logistics</div>
</div>
<div className="text-sm text-slate-700 dark:text-slate-200 leading-relaxed italic">"{data.ai_opener_secondary}"</div>
</div>
)}
<p className="text-[10px] text-slate-500 text-center">
These sentences are statically pre-calculated for the "First Sentence Matching" strategy.
</p>
</div>
</div>
)
}
// CRM Comparison and Data Quality Renderer
const renderDataQualityCard = () => {
if (!data) return null;
@@ -754,6 +795,7 @@ export function Inspector({ companyId, initialContactId, onClose, apiBase }: Ins
{renderDataQualityCard()}
{renderStrategyCard()}
{renderMarketingCard()}
<div className="bg-slate-50 dark:bg-slate-950 rounded-lg p-4 border border-slate-200 dark:border-slate-800 flex flex-col gap-2">
<div className="flex items-center justify-between mb-1">