Initial commit: Port Nimara CRM (Layers 0-4)
Full CRM rebuild with Next.js 15, TypeScript, Tailwind, Drizzle ORM,
PostgreSQL, Redis, BullMQ, MinIO, and Socket.io. Includes 461 source
files covering clients, berths, interests/pipeline, documents/EOI,
expenses/invoices, email, notifications, dashboard, admin, and
client portal. CI/CD via Gitea Actions with Docker builds.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 11:52:51 +01:00
|
|
|
import type { Template } from '@pdfme/common';
|
|
|
|
|
|
|
|
|
|
import type { PipelineData } from '@/lib/services/report-generators';
|
|
|
|
|
|
|
|
|
|
export const pipelineReportTemplate: Template = {
|
2026-03-26 12:06:18 +01:00
|
|
|
basePdf: 'BLANK_PDF' as unknown as string,
|
Initial commit: Port Nimara CRM (Layers 0-4)
Full CRM rebuild with Next.js 15, TypeScript, Tailwind, Drizzle ORM,
PostgreSQL, Redis, BullMQ, MinIO, and Socket.io. Includes 461 source
files covering clients, berths, interests/pipeline, documents/EOI,
expenses/invoices, email, notifications, dashboard, admin, and
client portal. CI/CD via Gitea Actions with Docker builds.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 11:52:51 +01:00
|
|
|
schemas: [
|
|
|
|
|
[
|
|
|
|
|
{
|
|
|
|
|
name: 'reportTitle',
|
|
|
|
|
type: 'text',
|
|
|
|
|
position: { x: 20, y: 15 },
|
|
|
|
|
width: 170,
|
|
|
|
|
height: 12,
|
|
|
|
|
fontSize: 20,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'portName',
|
|
|
|
|
type: 'text',
|
|
|
|
|
position: { x: 20, y: 30 },
|
|
|
|
|
width: 130,
|
|
|
|
|
height: 8,
|
|
|
|
|
fontSize: 11,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'generatedAt',
|
|
|
|
|
type: 'text',
|
|
|
|
|
position: { x: 140, y: 30 },
|
|
|
|
|
width: 50,
|
|
|
|
|
height: 8,
|
|
|
|
|
fontSize: 9,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'summaryText',
|
|
|
|
|
type: 'text',
|
|
|
|
|
position: { x: 20, y: 50 },
|
|
|
|
|
width: 170,
|
|
|
|
|
height: 100,
|
|
|
|
|
fontSize: 10,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'detailsText',
|
|
|
|
|
type: 'text',
|
|
|
|
|
position: { x: 20, y: 160 },
|
|
|
|
|
width: 170,
|
|
|
|
|
height: 100,
|
|
|
|
|
fontSize: 9,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
],
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export function buildPipelineInputs(
|
|
|
|
|
data: PipelineData,
|
|
|
|
|
portName?: string,
|
|
|
|
|
): Record<string, string>[] {
|
|
|
|
|
const stageOrder = [
|
|
|
|
|
'open',
|
|
|
|
|
'details_sent',
|
|
|
|
|
'in_communication',
|
2026-05-03 16:14:04 +02:00
|
|
|
'eoi_sent',
|
|
|
|
|
'eoi_signed',
|
Initial commit: Port Nimara CRM (Layers 0-4)
Full CRM rebuild with Next.js 15, TypeScript, Tailwind, Drizzle ORM,
PostgreSQL, Redis, BullMQ, MinIO, and Socket.io. Includes 461 source
files covering clients, berths, interests/pipeline, documents/EOI,
expenses/invoices, email, notifications, dashboard, admin, and
client portal. CI/CD via Gitea Actions with Docker builds.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 11:52:51 +01:00
|
|
|
'deposit_10pct',
|
2026-05-03 16:14:04 +02:00
|
|
|
'contract_sent',
|
|
|
|
|
'contract_signed',
|
Initial commit: Port Nimara CRM (Layers 0-4)
Full CRM rebuild with Next.js 15, TypeScript, Tailwind, Drizzle ORM,
PostgreSQL, Redis, BullMQ, MinIO, and Socket.io. Includes 461 source
files covering clients, berths, interests/pipeline, documents/EOI,
expenses/invoices, email, notifications, dashboard, admin, and
client portal. CI/CD via Gitea Actions with Docker builds.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 11:52:51 +01:00
|
|
|
'completed',
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const summaryLines = stageOrder
|
|
|
|
|
.filter((stage) => (data.stageCounts[stage] ?? 0) > 0)
|
|
|
|
|
.map((stage) => {
|
|
|
|
|
const label = stage.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
|
|
|
|
|
return `${label}: ${data.stageCounts[stage] ?? 0} interest(s)`;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Include stages not in standard order
|
2026-05-03 16:14:04 +02:00
|
|
|
const unknownStages = Object.keys(data.stageCounts).filter((s) => !stageOrder.includes(s));
|
Initial commit: Port Nimara CRM (Layers 0-4)
Full CRM rebuild with Next.js 15, TypeScript, Tailwind, Drizzle ORM,
PostgreSQL, Redis, BullMQ, MinIO, and Socket.io. Includes 461 source
files covering clients, berths, interests/pipeline, documents/EOI,
expenses/invoices, email, notifications, dashboard, admin, and
client portal. CI/CD via Gitea Actions with Docker builds.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 11:52:51 +01:00
|
|
|
for (const stage of unknownStages) {
|
|
|
|
|
summaryLines.push(`${stage}: ${data.stageCounts[stage]} interest(s)`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const totalInterests = Object.values(data.stageCounts).reduce((a, b) => a + b, 0);
|
|
|
|
|
summaryLines.unshift(`Total Active Interests: ${totalInterests}`);
|
|
|
|
|
summaryLines.unshift('Pipeline Stage Breakdown');
|
|
|
|
|
summaryLines.unshift('─────────────────────');
|
|
|
|
|
|
|
|
|
|
const detailLines = ['Top Interests by Value', '─────────────────────'];
|
|
|
|
|
if (data.topInterests.length === 0) {
|
|
|
|
|
detailLines.push('No interests with linked berths found.');
|
|
|
|
|
} else {
|
|
|
|
|
data.topInterests.forEach((interest, i) => {
|
|
|
|
|
const price = interest.berthPrice
|
|
|
|
|
? `Berth Price: ${Number(interest.berthPrice).toLocaleString()}`
|
|
|
|
|
: 'No berth linked';
|
|
|
|
|
const stage = interest.pipelineStage
|
|
|
|
|
.replace(/_/g, ' ')
|
|
|
|
|
.replace(/\b\w/g, (c) => c.toUpperCase());
|
|
|
|
|
detailLines.push(`${i + 1}. Stage: ${stage} | ${price}`);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
{
|
|
|
|
|
reportTitle: 'Pipeline Summary Report',
|
|
|
|
|
portName: portName ?? 'Port Nimara',
|
|
|
|
|
generatedAt: `Generated: ${new Date(data.generatedAt).toLocaleString('en-GB')}`,
|
|
|
|
|
summaryText: summaryLines.join('\n'),
|
|
|
|
|
detailsText: detailLines.join('\n'),
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
}
|