Update life quotes to use new policy API structure

- Update life quote types to use insured/buyer instead of client
- Update life fields: add coverage_type, coverage_amount, coverage_years, smoker, medications, surgeries, weight, height
- Remove deprecated fields: dateOfBirth, age, gender, preexistingConditions, preexistingDetails, beneficiaryName, beneficiaryRelationship
- Update life quote SetupStep component to use customer selection
- Add Insured and Buyer sections with customer search and selection
- Add health questionnaire with smoker, medications, surgeries, weight, height fields
- Add coverage_type field (banking | protection)
- Update life quote page to use new composables and API structure
- Update validation to use insured/buyer validation
- Update submission to use policy API with new structure
This commit is contained in:
2026-04-27 15:03:43 -05:00
parent a2eb1f3789
commit 6c411ce2b6
4 changed files with 289 additions and 153 deletions

View File

@@ -1,6 +1,8 @@
<script setup lang="ts">
import { emptyLifeQuoteDraft } from '~/composables/useLifeQuoteDraft'
import type { LifeQuoteIntakePayload, LifeQuoteMode, LifeQuoteSegment } from '~/types/life-quote-intake'
import { useCustomerSelection } from '~/composables/useCustomerSelection'
import { usePolicyApi } from '~/composables/usePolicyApi'
definePageMeta({ ssr: false })
@@ -24,6 +26,18 @@ const draft = reactive(emptyLifeQuoteDraft())
const toast = useToast()
const { quoteRequestEmailEnabled } = useQuoteRequestEmailEnabled()
// Use customer selection composable
const {
insured,
buyer,
isInsuredValid,
isBuyerValid,
validationErrors
} = useCustomerSelection()
// Use policy API composable
const { submitPolicyQuote } = usePolicyApi()
const modeCards: { id: LifeQuoteMode; title: string; hint: string; icon: string }[] = [
{
id: 'single',
@@ -60,19 +74,6 @@ const segmentCards: { id: LifeQuoteSegment; title: string; hint: string; icon: s
}
]
function canProceedFromCustomer() {
const c = draft.client
if (!c.fullName.trim() || !c.email.trim()) {
toast.add({
title: 'Add legal name and email',
description: 'We need them for notifications and the quote file.',
color: 'warning'
})
return false
}
return true
}
function canProceedFromSetup() {
if (!draft.quoteMode) {
toast.add({ title: 'Choose quote type', description: 'Single or comparative.', color: 'warning' })
@@ -82,30 +83,26 @@ function canProceedFromSetup() {
toast.add({ title: 'Choose policy type', description: 'Individual, corporate / key person, or group.', color: 'warning' })
return false
}
if (!canProceedFromCustomer()) return false
if (
(draft.segment === 'corporate_keyman' || draft.segment === 'group') &&
!draft.client.organizationName?.trim()
) {
if (!isInsuredValid.value) {
toast.add({
title: 'Add organization',
description: 'Required for corporate and group policies.',
title: 'Complete insured information',
description: `Missing: ${validationErrors.value.insured.join(', ')}`,
color: 'warning'
})
return false
}
if (!draft.life.dateOfBirth || !draft.life.gender) {
if (!isBuyerValid.value) {
toast.add({
title: 'Complete age & health screening',
description: 'Date of birth and gender are required for life underwriting.',
title: 'Complete buyer information',
description: `Missing: ${validationErrors.value.buyer.join(', ')}`,
color: 'warning'
})
return false
}
if (!draft.life.coverageAmount || !draft.life.coverageTerm) {
if (!draft.life.coverage_type || !draft.life.coverage_amount || !draft.life.coverage_years) {
toast.add({
title: 'Complete coverage intent',
description: 'Select coverage amount and term.',
title: 'Complete coverage details',
description: 'Coverage type, amount, and years are required.',
color: 'warning'
})
return false
@@ -170,17 +167,23 @@ function goNext() {
function buildPayload(): LifeQuoteIntakePayload {
return {
quoteMode: draft.quoteMode!,
segment: draft.segment!,
client: { ...draft.client },
life: { ...draft.life },
solicit: {
carrierIds: [...draft.solicit.carrierIds],
planIds: [...draft.solicit.planIds]
}
policy_type: 'life',
insured: insured.value,
buyer: buyer.value,
policy_details: { ...draft.life },
selected_providers: draft.solicit.carrierIds.map(id => ({
provider_id: id,
email: getProviderEmail(id)
}))
}
}
function getProviderEmail(providerId: string): string {
// This would come from the providers API
// For now, return a placeholder
return `quotes@${providerId}.com`
}
async function finalize() {
if (!draft.quoteMode || !draft.segment) return
if (intakeBusy.value) return
@@ -204,6 +207,10 @@ async function finalize() {
})
return
}
// Submit to policy API
const data = await submitPolicyQuote(payload)
toast.add({
title: emailOn ? 'Quote requests recorded' : 'Quote run saved (no emails)',
description: emailOn
@@ -211,6 +218,9 @@ async function finalize() {
: 'Outbound provider email is off in Settings — this request stays in-app for tables, APIs, or AI.',
color: 'success'
})
// Navigate to policy detail page
await navigateTo(`/policies/${data.application_id}`)
} finally {
intakeBusy.value = false
}