Fix financial table rendering and enhance PDF generation
- Fix [object Object] issue in PDF financial table rendering - Enhance Key Questions and Investment Thesis sections with detailed prompts - Update year labeling in Overview tab (FY0 -> LTM) - Improve PDF generation service with page pooling and caching - Add better error handling for financial data structure - Increase textarea rows for detailed content sections - Update API configuration for Cloud Run deployment - Add comprehensive styling improvements to PDF output
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
VITE_API_BASE_URL=https://us-central1-cim-summarizer.cloudfunctions.net/api
|
||||
VITE_API_BASE_URL=https://api-y56ccs6wva-uc.a.run.app
|
||||
VITE_FIREBASE_API_KEY=AIzaSyBoV04YHkbCSUIU6sXki57um4xNsvLV_jY
|
||||
VITE_FIREBASE_AUTH_DOMAIN=cim-summarizer.firebaseapp.com
|
||||
VITE_FIREBASE_PROJECT_ID=cim-summarizer
|
||||
|
||||
7
frontend/.env.production.backup
Normal file
7
frontend/.env.production.backup
Normal file
@@ -0,0 +1,7 @@
|
||||
VITE_API_BASE_URL=https://us-central1-cim-summarizer.cloudfunctions.net/api
|
||||
VITE_FIREBASE_API_KEY=AIzaSyBoV04YHkbCSUIU6sXki57um4xNsvLV_jY
|
||||
VITE_FIREBASE_AUTH_DOMAIN=cim-summarizer.firebaseapp.com
|
||||
VITE_FIREBASE_PROJECT_ID=cim-summarizer
|
||||
VITE_FIREBASE_STORAGE_BUCKET=cim-summarizer.firebasestorage.app
|
||||
VITE_FIREBASE_MESSAGING_SENDER_ID=245796323861
|
||||
VITE_FIREBASE_APP_ID=1:245796323861:web:39c1c86e0e4b405510041c
|
||||
@@ -79,19 +79,27 @@ const Dashboard: React.FC = () => {
|
||||
const documentsArray = result.documents || result;
|
||||
if (Array.isArray(documentsArray)) {
|
||||
// Transform backend data to frontend format
|
||||
const transformedDocs = documentsArray.map((doc: any) => ({
|
||||
id: doc.id,
|
||||
name: doc.name || doc.originalName || 'Unknown',
|
||||
originalName: doc.originalName || doc.name || 'Unknown',
|
||||
status: mapBackendStatus(doc.status),
|
||||
uploadedAt: doc.uploadedAt,
|
||||
processedAt: doc.processedAt,
|
||||
uploadedBy: user?.name || user?.email || 'Unknown',
|
||||
fileSize: parseInt(doc.fileSize) || 0,
|
||||
summary: doc.summary,
|
||||
error: doc.error,
|
||||
analysisData: doc.extractedData, // Include the enhanced BPCP CIM Review Template data
|
||||
}));
|
||||
const transformedDocs = documentsArray.map((doc: any) => {
|
||||
// Extract company name from analysis data if available
|
||||
let displayName = doc.name || doc.originalName || 'Unknown';
|
||||
if (doc.analysis_data && doc.analysis_data.dealOverview && doc.analysis_data.dealOverview.targetCompanyName) {
|
||||
displayName = doc.analysis_data.dealOverview.targetCompanyName;
|
||||
}
|
||||
|
||||
return {
|
||||
id: doc.id,
|
||||
name: displayName,
|
||||
originalName: doc.originalName || doc.name || 'Unknown',
|
||||
status: mapBackendStatus(doc.status),
|
||||
uploadedAt: doc.uploadedAt,
|
||||
processedAt: doc.processedAt,
|
||||
uploadedBy: user?.name || user?.email || 'Unknown',
|
||||
fileSize: parseInt(doc.fileSize) || 0,
|
||||
summary: doc.summary,
|
||||
error: doc.error,
|
||||
analysisData: doc.extractedData, // Include the enhanced BPCP CIM Review Template data
|
||||
};
|
||||
});
|
||||
setDocuments(transformedDocs);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -194,6 +194,7 @@ const CIMReviewTemplate: React.FC<CIMReviewTemplateProps> = ({
|
||||
// Merge cimReviewData with existing data when it changes
|
||||
useEffect(() => {
|
||||
if (cimReviewData && Object.keys(cimReviewData).length > 0) {
|
||||
console.log('CIMReviewTemplate: Received cimReviewData:', cimReviewData);
|
||||
setData(prev => ({
|
||||
...prev,
|
||||
...cimReviewData
|
||||
@@ -201,6 +202,29 @@ const CIMReviewTemplate: React.FC<CIMReviewTemplateProps> = ({
|
||||
}
|
||||
}, [cimReviewData]);
|
||||
|
||||
// Ensure financial data structure is properly initialized
|
||||
useEffect(() => {
|
||||
console.log('CIMReviewTemplate: Current data state:', data);
|
||||
console.log('CIMReviewTemplate: Financial summary data:', data.financialSummary);
|
||||
|
||||
// Ensure financial data structure exists
|
||||
if (!data.financialSummary?.financials) {
|
||||
console.log('CIMReviewTemplate: Initializing financial data structure');
|
||||
setData(prev => ({
|
||||
...prev,
|
||||
financialSummary: {
|
||||
...prev.financialSummary,
|
||||
financials: {
|
||||
fy3: { revenue: '', revenueGrowth: '', grossProfit: '', grossMargin: '', ebitda: '', ebitdaMargin: '' },
|
||||
fy2: { revenue: '', revenueGrowth: '', grossProfit: '', grossMargin: '', ebitda: '', ebitdaMargin: '' },
|
||||
fy1: { revenue: '', revenueGrowth: '', grossProfit: '', grossMargin: '', ebitda: '', ebitdaMargin: '' },
|
||||
ltm: { revenue: '', revenueGrowth: '', grossProfit: '', grossMargin: '', ebitda: '', ebitdaMargin: '' },
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
}, [data.financialSummary?.financials]);
|
||||
|
||||
|
||||
|
||||
const updateFinancials = (period: keyof CIMReviewData['financialSummary']['financials'], field: string, value: string) => {
|
||||
@@ -313,7 +337,35 @@ const CIMReviewTemplate: React.FC<CIMReviewTemplateProps> = ({
|
||||
return typeof current === 'string' ? current : '';
|
||||
};
|
||||
|
||||
const renderFinancialTable = () => (
|
||||
const renderFinancialTable = () => {
|
||||
console.log('CIMReviewTemplate: Rendering financial table');
|
||||
console.log('CIMReviewTemplate: Financial data:', data.financialSummary?.financials);
|
||||
console.log('CIMReviewTemplate: Full data object:', data);
|
||||
|
||||
// Ensure financial data exists and handle different data formats
|
||||
let financials = data.financialSummary?.financials;
|
||||
|
||||
if (!financials || typeof financials !== 'object') {
|
||||
console.log('CIMReviewTemplate: No financial data found, using default structure');
|
||||
financials = {
|
||||
fy3: { revenue: '', revenueGrowth: '', grossProfit: '', grossMargin: '', ebitda: '', ebitdaMargin: '' },
|
||||
fy2: { revenue: '', revenueGrowth: '', grossProfit: '', grossMargin: '', ebitda: '', ebitdaMargin: '' },
|
||||
fy1: { revenue: '', revenueGrowth: '', grossProfit: '', grossMargin: '', ebitda: '', ebitdaMargin: '' },
|
||||
ltm: { revenue: '', revenueGrowth: '', grossProfit: '', grossMargin: '', ebitda: '', ebitdaMargin: '' },
|
||||
};
|
||||
}
|
||||
|
||||
// Ensure each period has the required structure
|
||||
const periods = ['fy3', 'fy2', 'fy1', 'ltm'] as const;
|
||||
periods.forEach(period => {
|
||||
if (!financials[period] || typeof financials[period] !== 'object') {
|
||||
financials[period] = { revenue: '', revenueGrowth: '', grossProfit: '', grossMargin: '', ebitda: '', ebitdaMargin: '' };
|
||||
}
|
||||
});
|
||||
|
||||
console.log('CIMReviewTemplate: Processed financials:', financials);
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<h4 className="text-lg font-medium text-gray-900">Key Historical Financials</h4>
|
||||
<div className="overflow-x-auto">
|
||||
@@ -346,7 +398,7 @@ const CIMReviewTemplate: React.FC<CIMReviewTemplateProps> = ({
|
||||
<td key={period} className="px-6 py-4 whitespace-nowrap">
|
||||
<input
|
||||
type="text"
|
||||
value={data.financialSummary.financials[period].revenue}
|
||||
value={financials[period]?.revenue || ''}
|
||||
onChange={(e) => updateFinancials(period, 'revenue', e.target.value)}
|
||||
placeholder="$0"
|
||||
disabled={readOnly}
|
||||
@@ -363,7 +415,7 @@ const CIMReviewTemplate: React.FC<CIMReviewTemplateProps> = ({
|
||||
<td key={period} className="px-6 py-4 whitespace-nowrap">
|
||||
<input
|
||||
type="text"
|
||||
value={data.financialSummary.financials[period].revenueGrowth}
|
||||
value={financials[period]?.revenueGrowth || ''}
|
||||
onChange={(e) => updateFinancials(period, 'revenueGrowth', e.target.value)}
|
||||
placeholder="0%"
|
||||
disabled={readOnly}
|
||||
@@ -380,7 +432,7 @@ const CIMReviewTemplate: React.FC<CIMReviewTemplateProps> = ({
|
||||
<td key={period} className="px-6 py-4 whitespace-nowrap">
|
||||
<input
|
||||
type="text"
|
||||
value={data.financialSummary.financials[period].ebitda}
|
||||
value={financials[period]?.ebitda || ''}
|
||||
onChange={(e) => updateFinancials(period, 'ebitda', e.target.value)}
|
||||
placeholder="$0"
|
||||
disabled={readOnly}
|
||||
@@ -397,7 +449,7 @@ const CIMReviewTemplate: React.FC<CIMReviewTemplateProps> = ({
|
||||
<td key={period} className="px-6 py-4 whitespace-nowrap">
|
||||
<input
|
||||
type="text"
|
||||
value={data.financialSummary.financials[period].ebitdaMargin}
|
||||
value={financials[period].ebitdaMargin}
|
||||
onChange={(e) => updateFinancials(period, 'ebitdaMargin', e.target.value)}
|
||||
placeholder="0%"
|
||||
disabled={readOnly}
|
||||
@@ -410,7 +462,8 @@ const CIMReviewTemplate: React.FC<CIMReviewTemplateProps> = ({
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
||||
const renderSection = () => {
|
||||
switch (activeSection) {
|
||||
@@ -510,21 +563,21 @@ const CIMReviewTemplate: React.FC<CIMReviewTemplateProps> = ({
|
||||
case 'investment-thesis':
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{renderField('Key Attractions / Strengths (Why Invest?)', 'preliminaryInvestmentThesis.keyAttractions', 'textarea', 'List key attractions...', 4)}
|
||||
{renderField('Potential Risks / Concerns (Why Not Invest?)', 'preliminaryInvestmentThesis.potentialRisks', 'textarea', 'List potential risks...', 4)}
|
||||
{renderField('Initial Value Creation Levers (How PE Adds Value)', 'preliminaryInvestmentThesis.valueCreationLevers', 'textarea', 'Identify value creation levers...', 4)}
|
||||
{renderField('Alignment with Fund Strategy', 'preliminaryInvestmentThesis.alignmentWithFundStrategy', 'textarea', 'Assess alignment with BPCP strategy...', 4)}
|
||||
{renderField('Key Attractions / Strengths (Why Invest?)', 'preliminaryInvestmentThesis.keyAttractions', 'textarea', 'List key attractions...', 8)}
|
||||
{renderField('Potential Risks / Concerns (Why Not Invest?)', 'preliminaryInvestmentThesis.potentialRisks', 'textarea', 'List potential risks...', 8)}
|
||||
{renderField('Initial Value Creation Levers (How PE Adds Value)', 'preliminaryInvestmentThesis.valueCreationLevers', 'textarea', 'Identify value creation levers...', 8)}
|
||||
{renderField('Alignment with Fund Strategy', 'preliminaryInvestmentThesis.alignmentWithFundStrategy', 'textarea', 'Assess alignment with BPCP strategy...', 8)}
|
||||
</div>
|
||||
);
|
||||
|
||||
case 'next-steps':
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{renderField('Critical Questions Arising from CIM Review', 'keyQuestionsNextSteps.criticalQuestions', 'textarea', 'List critical questions...', 4)}
|
||||
{renderField('Key Missing Information / Areas for Diligence Focus', 'keyQuestionsNextSteps.missingInformation', 'textarea', 'Identify missing information...', 4)}
|
||||
{renderField('Critical Questions Arising from CIM Review', 'keyQuestionsNextSteps.criticalQuestions', 'textarea', 'List critical questions...', 8)}
|
||||
{renderField('Key Missing Information / Areas for Diligence Focus', 'keyQuestionsNextSteps.missingInformation', 'textarea', 'Identify missing information...', 8)}
|
||||
{renderField('Preliminary Recommendation', 'keyQuestionsNextSteps.preliminaryRecommendation')}
|
||||
{renderField('Rationale for Recommendation (Brief)', 'keyQuestionsNextSteps.rationaleForRecommendation', 'textarea', 'Provide rationale...', 4)}
|
||||
{renderField('Proposed Next Steps', 'keyQuestionsNextSteps.proposedNextSteps', 'textarea', 'Outline next steps...', 4)}
|
||||
{renderField('Rationale for Recommendation (Brief)', 'keyQuestionsNextSteps.rationaleForRecommendation', 'textarea', 'Provide rationale...', 6)}
|
||||
{renderField('Proposed Next Steps', 'keyQuestionsNextSteps.proposedNextSteps', 'textarea', 'Outline next steps...', 8)}
|
||||
</div>
|
||||
);
|
||||
|
||||
|
||||
@@ -228,7 +228,7 @@ const DocumentViewer: React.FC<DocumentViewerProps> = ({
|
||||
<div className="space-y-1">
|
||||
{extractedData.financials.revenue.map((value, index) => (
|
||||
<div key={index} className="flex justify-between text-sm">
|
||||
<span className="text-gray-600">FY{3-index}</span>
|
||||
<span className="text-gray-600">{index === 3 ? 'LTM' : `FY${3-index}`}</span>
|
||||
<span className="font-medium">{value}</span>
|
||||
</div>
|
||||
))}
|
||||
@@ -239,7 +239,7 @@ const DocumentViewer: React.FC<DocumentViewerProps> = ({
|
||||
<div className="space-y-1">
|
||||
{extractedData.financials.ebitda.map((value, index) => (
|
||||
<div key={index} className="flex justify-between text-sm">
|
||||
<span className="text-gray-600">FY{3-index}</span>
|
||||
<span className="text-gray-600">{index === 3 ? 'LTM' : `FY${3-index}`}</span>
|
||||
<span className="font-medium">{value}</span>
|
||||
</div>
|
||||
))}
|
||||
@@ -250,7 +250,7 @@ const DocumentViewer: React.FC<DocumentViewerProps> = ({
|
||||
<div className="space-y-1">
|
||||
{extractedData.financials.margins.map((value, index) => (
|
||||
<div key={index} className="flex justify-between text-sm">
|
||||
<span className="text-gray-600">FY{3-index}</span>
|
||||
<span className="text-gray-600">{index === 3 ? 'LTM' : `FY${3-index}`}</span>
|
||||
<span className="font-medium">{value}</span>
|
||||
</div>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user