Files
whyrating-engine-legacy/web/components/reviewiq/ReportTab.tsx
Alejandro Gutiérrez d5ef13b58e feat(frontend): Add BusinessReport component for 6-section €60 report
- Create BusinessReport.tsx with 6 sections:
  1. Executive Summary (health score, rating, momentum)
  2. Risk Scorecard (indicators with colors/trends)
  3. Critical Issues (evidence, solutions, timelines)
  4. Strengths to Protect (quotes, leverage actions)
  5. Action Matrix (effort/impact quadrants)
  6. 90-Day Tracking (KPI targets table)

- Update types.ts with new interfaces:
  - SynthesisV2 for new report format
  - LegacySynthesis for backwards compatibility
  - Type guard isSynthesisV2() for runtime detection

- Update ReportTab to auto-detect synthesis version
- Update AnalystReport, ReviewIQDashboard, StoryView
  for backwards compatibility with union type

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 14:36:05 +00:00

102 lines
2.9 KiB
TypeScript

'use client';
import { Loader2, FileWarning } from 'lucide-react';
import { useReviewIQAnalytics } from '@/hooks/useReviewIQAnalytics';
import { AnalystReport } from './AnalystReport';
import { BusinessReport } from './BusinessReport';
import { isSynthesisV2 } from './types';
import type { ReviewIQFilters, LegacySynthesis, SynthesisV2 } from './types';
interface ReportTabProps {
jobId?: string;
businessId?: string;
}
// Default filters for Report view - uses 'all' time range for comprehensive analysis
const defaultFilters: ReviewIQFilters = {
timeRange: 'all',
sentiment: [],
urtDomain: null,
intensity: [],
brushRange: null,
};
/**
* Report Tab - Wraps report components with data fetching.
* Automatically detects report version and renders appropriate component.
*/
export function ReportTab({ jobId, businessId }: ReportTabProps) {
const { data, loading, error } = useReviewIQAnalytics({
jobId,
businessId,
filters: defaultFilters,
});
// Loading state
if (loading) {
return (
<div className="flex items-center justify-center min-h-[400px]">
<Loader2 className="w-8 h-8 animate-spin text-indigo-600" />
</div>
);
}
// Error state
if (error) {
return (
<div className="bg-red-50 border border-red-200 rounded-lg p-6 text-center">
<p className="text-red-700">{error}</p>
</div>
);
}
// No data state
if (!data) {
return (
<div className="bg-slate-50 border border-slate-200 rounded-lg p-6 text-center">
<p className="text-slate-600">No data available for this report.</p>
</div>
);
}
// No synthesis - AI report not generated yet
if (!data.synthesis) {
return (
<div className="max-w-2xl mx-auto mt-12">
<div className="bg-amber-50 border border-amber-200 rounded-2xl p-8 text-center">
<FileWarning className="w-12 h-12 text-amber-500 mx-auto mb-4" />
<h3 className="text-xl font-semibold text-amber-900 mb-2">
AI Report Not Generated Yet
</h3>
<p className="text-amber-700 mb-4">
The AI-powered analyst report hasn't been generated for this dataset.
Run the pipeline with the "synthesize" stage to generate the report.
</p>
<div className="text-sm text-amber-600 bg-amber-100 rounded-lg p-3 font-mono">
Stage 5: Synthesize Generates narratives, actions & insights
</div>
</div>
</div>
);
}
// Render the appropriate report based on synthesis version
if (isSynthesisV2(data.synthesis)) {
// New 6-section Business Report (v2.0)
return <BusinessReport synthesis={data.synthesis} />;
}
// Legacy Analyst Report (v1.x)
return (
<AnalystReport
synthesis={data.synthesis}
overview={data.overview}
sentiment={data.sentiment.distribution}
domains={data.urt.domains}
timeline={data.timeline}
/>
);
}
export default ReportTab;