feat(eoi): template-aware generate-EOI dialog

The EOI dialog now lists "Documenso Standard EOI" (default) plus any
seeded in-app EOI templates and routes the submit to the dual-path
generate-and-sign endpoint with the correct pathway:

  - "documenso-template" sentinel id → pathway: documenso-template
  - any other template id → pathway: inapp

Signers are derived server-side from EoiContext for both pathways when
the template type is EOI (interest's client + hardcoded developer +
approver), so the dialog doesn't collect them. Non-EOI templates still
require explicit signers.

Drops the legacy `client.yachtLengthFt` prerequisite check (yacht is now
a first-class entity) and replaces it with hasYacht based on
interest.yachtId. Tests updated; 646/646 green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matt Ciaccio
2026-04-26 13:42:08 +02:00
parent 2ff24a7132
commit f4ec51002c
4 changed files with 171 additions and 58 deletions

View File

@@ -14,13 +14,9 @@ interface InterestDocumentsTabProps {
interface InterestData {
id: string;
yachtId?: string | null;
berthId?: string | null;
client?: {
fullName?: string | null;
yachtLengthFt?: string | null;
yachtLengthM?: string | null;
contacts?: Array<{ channel: string; value: string }>;
};
clientName?: string | null;
}
export function InterestDocumentsTab({ interestId }: InterestDocumentsTabProps) {
@@ -28,20 +24,14 @@ export function InterestDocumentsTab({ interestId }: InterestDocumentsTabProps)
const { data: interestRes } = useQuery({
queryKey: ['interests', interestId],
queryFn: () =>
apiFetch<{ data: InterestData }>(`/api/v1/interests/${interestId}`),
queryFn: () => apiFetch<{ data: InterestData }>(`/api/v1/interests/${interestId}`),
});
const interest = interestRes?.data;
const prerequisites = {
hasName: Boolean(interest?.client?.fullName),
hasEmail: Boolean(
interest?.client?.contacts?.some((c) => c.channel === 'email' && c.value),
),
hasYachtDims: Boolean(
interest?.client?.yachtLengthFt || interest?.client?.yachtLengthM,
),
hasName: Boolean(interest?.clientName),
hasYacht: Boolean(interest?.yachtId),
hasBerth: Boolean(interest?.berthId),
};