feat(recommender): API endpoint + interest-detail panel + add-to-interest dialog

This commit is contained in:
Matt Ciaccio
2026-05-05 03:05:22 +02:00
parent e00e812199
commit 15d4849030
6 changed files with 762 additions and 0 deletions

View File

@@ -12,6 +12,7 @@ import { NotesList } from '@/components/shared/notes-list';
import { InlineEditableField } from '@/components/shared/inline-editable-field';
import { InlineTagEditor } from '@/components/shared/inline-tag-editor';
import { RecommendationList } from '@/components/interests/recommendation-list';
import { BerthRecommenderPanel } from '@/components/interests/berth-recommender-panel';
import { InterestTimeline } from '@/components/interests/interest-timeline';
import { InterestDocumentsTab } from '@/components/interests/interest-documents-tab';
import { InterestFilesTab } from '@/components/interests/interest-files-tab';
@@ -37,6 +38,10 @@ interface InterestTabsOptions {
currentUserId?: string;
interest: {
pipelineStage: string;
/** Drives the recommender panel mounted on the Overview tab. */
desiredLengthFt?: string | null;
desiredWidthFt?: string | null;
desiredDraftFt?: string | null;
leadCategory: string | null;
source: string | null;
eoiStatus: string | null;
@@ -306,6 +311,12 @@ function OverviewTab({
activeMilestone = 'contract';
}
const toNum = (v: string | null | undefined): number | null => {
if (v === null || v === undefined) return null;
const n = parseFloat(v);
return Number.isFinite(n) ? n : null;
};
return (
<div className="space-y-6">
{/* Sales-process milestones - the heart of the system. Each section is a
@@ -498,6 +509,16 @@ function OverviewTab({
/>
</div>
</div>
{/* Berth recommender (plan §5.3) - always-mounted card driven by the
interest's desired dimensions. Renders an inline guidance message
when dimensions aren't set yet. */}
<BerthRecommenderPanel
interestId={interestId}
desiredLengthFt={toNum(interest.desiredLengthFt)}
desiredWidthFt={toNum(interest.desiredWidthFt)}
desiredDraftFt={toNum(interest.desiredDraftFt)}
/>
</div>
);
}