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' ;
export const eoiTemplate : 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 : 'portName' , type : 'text' , position : { x : 20 , y : 20 } , width : 170 , height : 10 , fontSize : 18 } ,
{ name : 'title' , type : 'text' , position : { x : 20 , y : 40 } , width : 170 , height : 8 , fontSize : 14 } ,
{ name : 'clientName' , type : 'text' , position : { x : 20 , y : 60 } , width : 80 , height : 6 } ,
{ name : 'clientEmail' , type : 'text' , position : { x : 20 , y : 68 } , width : 80 , height : 6 } ,
{ name : 'yachtName' , type : 'text' , position : { x : 20 , y : 80 } , width : 80 , height : 6 } ,
{ name : 'yachtDimensions' , type : 'text' , position : { x : 20 , y : 88 } , width : 80 , height : 6 } ,
{ name : 'berthNumber' , type : 'text' , position : { x : 110 , y : 60 } , width : 80 , height : 6 } ,
{ name : 'berthDimensions' , type : 'text' , position : { x : 110 , y : 68 } , width : 80 , height : 6 } ,
{ name : 'berthPrice' , type : 'text' , position : { x : 110 , y : 76 } , width : 80 , height : 6 } ,
{ name : 'date' , type : 'text' , position : { x : 20 , y : 110 } , width : 80 , height : 6 } ,
{ name : 'terms' , type : 'text' , position : { x : 20 , y : 130 } , width : 170 , height : 100 , fontSize : 9 } ,
] ,
] ,
} ;
export function buildEoiInputs (
interest : Record < string , unknown > ,
client : Record < string , unknown > ,
berth : Record < string , unknown > ,
port : Record < string , unknown > ,
) : Record < string , string > {
const contacts = ( client . contacts as Array < { channel : string ; value : string } > | undefined ) ? ? [ ] ;
const emailContact = contacts . find ( ( c ) = > c . channel === 'email' ) ;
return {
portName : ( port . name as string ) ? ? 'Port Nimara' ,
title : 'Expression of Interest' ,
clientName : ` Client: ${ client . fullName as string } ` ,
clientEmail : ` Email: ${ emailContact ? . value ? ? 'N/A' } ` ,
yachtName : ` Yacht: ${ ( client . yachtName as string ) ? ? 'N/A' } ` ,
yachtDimensions : ` LOA: ${ ( client . yachtLengthFt as string ) ? ? '?' } ft × Beam: ${ ( client . yachtWidthFt as string ) ? ? '?' } ft × Draft: ${ ( client . yachtDraftFt as string ) ? ? '?' } ft ` ,
berthNumber : ` Berth: ${ berth . mooringNumber as string } ` ,
berthDimensions : ` ${ ( berth . lengthFt as string ) ? ? '?' } ft × ${ ( berth . widthFt as string ) ? ? '?' } ft ` ,
berthPrice : ` Price: ${ ( berth . priceCurrency as string ) ? ? 'USD' } ${ ( berth . price as string ) ? ? 'TBD' } ` ,
date : ` Date: ${ new Date ( ) . toLocaleDateString ( 'en-GB' ) } ` ,
terms :
"This Expression of Interest confirms the above-named client's interest in the specified berth. This document is non-binding until signed by all parties. Upon signing, the client agrees to proceed with the berth acquisition process as outlined in the full terms and conditions provided separately." ,
} ;
}