// ─── Pipeline Stages ───────────────────────────────────────────────────────── // // 7 canonical stages (one optional). Document-signing stages (EOI, Reservation, // Contract) collapse "Sent + Signed" into one stage; the sub-status lives on // per-stage doc-status columns (`eoi_doc_status`, etc.) and is rendered as a // badge inside the kanban card. // // `nurturing` is built but disabled-by-default for ports that don't have // supply constraints (e.g. Port Nimara pre-launch). Admins enable it per port. export const PIPELINE_STAGES = [ 'enquiry', 'qualified', 'nurturing', 'eoi', 'reservation', 'deposit_paid', 'contract', ] as const; export type PipelineStage = (typeof PIPELINE_STAGES)[number]; /** * Sub-status values for document-signing stages (EOI, Reservation, Contract). * Stored on per-stage columns `eoi_doc_status` / `reservation_doc_status` / * `contract_doc_status` on the interests table. */ export const DOC_STATUSES = ['pending', 'sent', 'signed', 'declined', 'voided'] as const; export type DocStatus = (typeof DOC_STATUSES)[number]; export const STAGE_LABELS: Record = { enquiry: 'New Enquiry', qualified: 'Qualified', nurturing: 'Nurturing', eoi: 'EOI', reservation: 'Reservation', deposit_paid: 'Deposit Paid', contract: 'Contract', }; // Compact labels for cramped contexts (mobile chart axes, dense tables). export const STAGE_SHORT_LABELS: Record = { enquiry: 'Enquiry', qualified: 'Qual.', nurturing: 'Nurt.', eoi: 'EOI', reservation: 'Resv.', deposit_paid: 'Dep.', contract: 'Contract', }; export const STAGE_BADGE: Record = { enquiry: 'bg-slate-100 text-slate-700', qualified: 'bg-blue-100 text-blue-700', nurturing: 'bg-purple-100 text-purple-700', eoi: 'bg-indigo-100 text-indigo-700', reservation: 'bg-amber-100 text-amber-700', deposit_paid: 'bg-orange-100 text-orange-700', contract: 'bg-green-100 text-green-700', }; export const STAGE_DOT: Record = { enquiry: 'bg-slate-400', qualified: 'bg-blue-500', nurturing: 'bg-purple-500', eoi: 'bg-indigo-500', reservation: 'bg-amber-500', deposit_paid: 'bg-orange-500', contract: 'bg-green-500', }; // Default revenue-forecast probability weights per stage (0–1). // Editable per port via settings (`pipeline_weights`); these are the fallbacks. export const STAGE_WEIGHTS: Record = { enquiry: 0.05, qualified: 0.15, nurturing: 0.15, eoi: 0.4, reservation: 0.7, deposit_paid: 0.85, contract: 0.95, }; /** * Allowed transitions out of each stage. Skip-aheads (e.g. enquiry → * deposit_paid) are gated by the explicit `override:true` path in * `changeInterestStage` and surface as a backfill banner on the interest. * * Nurturing is bidirectional with qualified (deal pauses → reopens), * and can re-enter the EOI path when supply opens up. */ export const STAGE_TRANSITIONS: Record = { enquiry: ['qualified', 'eoi'], qualified: ['enquiry', 'nurturing', 'eoi'], nurturing: ['qualified', 'eoi'], eoi: ['qualified', 'reservation', 'deposit_paid'], reservation: ['eoi', 'deposit_paid'], deposit_paid: ['reservation', 'contract'], contract: ['deposit_paid'], }; export function canTransitionStage(from: string, to: string): boolean { if (from === to) return true; const fromStage = safeStage(from); const toStage = safeStage(to); return STAGE_TRANSITIONS[fromStage].includes(toStage); } export function safeStage(value: string | null | undefined): PipelineStage { return PIPELINE_STAGES.includes(value as PipelineStage) ? (value as PipelineStage) : 'enquiry'; } export function stageLabel(stage: string | null | undefined): string { return STAGE_LABELS[safeStage(stage)]; } export function stageBadgeClass(stage: string | null | undefined): string { return STAGE_BADGE[safeStage(stage)]; } export function stageDotClass(stage: string | null | undefined): string { return STAGE_DOT[safeStage(stage)]; } // ─── Berth Statuses ────────────────────────────────────────────────────────── export const BERTH_STATUSES = ['available', 'under_offer', 'sold'] as const; export type BerthStatus = (typeof BERTH_STATUSES)[number]; // ─── Berth single-select catalogues (mirror NocoDB) ────────────────────────── // Stored as free text in the DB so legacy values still load, but the form // presents only the canonical options below. export const BERTH_AREAS = ['A', 'B', 'C', 'D', 'E'] as const; export const BERTH_BOW_FACING_OPTIONS = ['North', 'South', 'East', 'West'] as const; export const BERTH_SIDE_PONTOON_OPTIONS = [ 'No', 'Quay SB', 'Quay PT', 'Quay SB, Yes PT', 'Quay PT, Yes SB', 'Yes SB', 'Yes PT', 'Yes SB, PT', 'Finger SB', 'Finger PT', ] as const; export const BERTH_MOORING_TYPES = [ 'Side Pier / Med Mooring', '2x Med Mooring', 'Side Pier / Finger', 'Finger / Med Mooring', '2x Finger', ] as const; export const BERTH_CLEAT_TYPES = ['A3', 'A5'] as const; export const BERTH_CLEAT_CAPACITIES = ['10-14 ton break load', '20-24 ton break load'] as const; export const BERTH_BOLLARD_TYPES = ['Bull bollard type A', 'Bull bollard type B'] as const; export const BERTH_BOLLARD_CAPACITIES = ['20 ton break load', '40 ton break load'] as const; export const BERTH_ACCESS_OPTIONS = [ 'Car to Vessel', 'Car to Quai, Cart to Vessel', 'Cart to Vessel', 'Car (3t) to Vessel', 'Car (3.5t) to Vessel', ] as const; /** * Map a readonly enum tuple into shadcn `